编写易于维护的代码, 其中一个最重要方面是能够找到代码中重复出现的主题并优化他们, 这也是设计模式有价值的地方, 设计模式不仅适用于原生javascript(既标准javascript代码), 也适用于jQuery库和Dojo等抽象库, 以下内容就是对这本设计模式做的一个读书笔记.

什么是模式?

  模式是一种可复用的解决方案, 可用于解决软件设计中遇到的常见问题, 如在我们编写的Javascript应用程序的实例中, 另一种模式的方式是将解决问题的方法制作成模板, 并且这些模板可应用于多种不同的情况.

反模式

  • 在全局上下文中定义大量的变量污染全局命名空间
  • 向 setTimeout 和 setInterval 传递字符串, 而不是函数, 这会触发eval() 的内部使用
  • 修改 Object 类的原型 (这是一个特别不良的反模式)
  • 以内联形式使用 Javascript , 它是不可改变的
  • 在使用 document.creatElement 等原生 DOM 方法更合适的情况下使用 document.write

设计模式类型

创建型设计模式

  • 创建型设计模式专注于处理对象创建机制, 以适合给定情况的方式来创建对象
  • 属于这个类型的有: Constructor(构造器) Factory(工厂) Abstract(抽象) Prototype(原型) Singleton(单例) 和 Builder(生成器)

结构型设计模式

  • 与对象组合有关, 通常用于找出在不同对象之间建立关系的简单方法
  • 属于这个类型的有: Decorator(装饰器) Facade(外观) Flyweight(享元) Adapter(适配器) 和 Proxy(代理)

行为设计模式

  • 专注于改善或简化系统中不同对象之间的通信
  • 属于这个类型的有: Iterator(迭代器) Mediator(中介者) Observer(观察者) 和 Visitor(访问者)

Javascript设计模式

Constructor (构造器) 模式

    Constructor是一种在内存已分配给该对象的情况下, 用于初始化新创建对象的特殊方法, 我们最感兴趣的是 Object 构造器 (用于创建特定类型的对象)

对象创建

  • 创建对象的两种常用方法

    1
    2
    3
    4
    // 1. 字面量的方法
    var newObj = {}
    // 2. object构造器的简洁记法
    var newObj = new Object()
  • 给对象赋值的四种方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // 1. '点' 的语法
    newObj.someKey = 'Hello world'; // 设置属性
    var key = newObj.someKey; // 获取属性

    // 2. 中括号语法
    newObj['someKey'] = 'Hello world'; // 设置属性
    var key = newObj['someKey']; // 获取属性

    **下面两个只适用ECMAScript 5**

    // 3. Object.defineProperty
    var defineProp = function(obj, key, value) {
    config.value = value;
    Object.defineProperty(obj, key, config)
    }
    // 先创建一个空的person对象
    var person = Object.create(null);
    // 然后设置各种属性
    defineProp.(person, 'car','Delorean);
    defineProp.(person, 'dateOfBirth','1981);

    // 4. Object.defineProperties
    Object.defineProperties(newObj, {
    'someKey': {
    value: 'hello world',
    writable: true
    },
    'authorKey': {
    value: 'Carol',
    writable: false
    }
    })

    //可以用1和2中获取属性的方式获取3和4方式中的属性**

基本 Constructor (构造器)

通过在构造器前面加new关键字, 告诉Javascript像使用构造器一样实例化一个新对象, 并且对象成员由该函数定义, 在构造器内, 关键字this引用新创建的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 定义一个构造函数
function Car(model, year , miles) {
this.model = model;
this.year = year;
this.miles = miles;

this.toString = function () {
return this.model + ' has done ' + this.miles + miles;
}
}

// 用法, 使用new关键字创建car的实例
var civic = new Car('BMW', 2009, 20000);
var mondeo = new Car('Ford Mondeo', 2009, 20000);

// 每一个实例都可以使用toString方法
console.log(civic.toString())

这里有两个问题: 一个是继承比较困难, 第二个是 toString方法是为每一个实例分别重新定义的, 这不太理想

带原型的 Constructor (构造器)

1
2
3
4
5
6
// 改造一下上面一个构造函数的写法
Car.prototype.toString = function(){
return this.model + ' has done ' + this.miles + miles;
}

**__这样的话, toString()的单一实例就能在所有的Car实例之间共享__**

我们这里可以使用Object.prototype.newMethod, 而不使用Object.prototype是为了避免重新定义prototype对象

Module (模块) 模式

在Javascript中, 有几种实现模块的方法 (Module模式在某种程度上是基于对象字面量)

  • 对象字面量表示法
  • Module 模块
  • AMD 模块
  • CommonJS 模块
  • ECMAScript Harmony 模块

对象字面量

对象字面量不需要使用 new 关键字进行实例化, 但不能用在一个语句的开头, 对象字面量可以包含属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
var myModule = {
myProperty: 'someKey',
myConfig : {
useCacheing: true
},
// 方法
myMethod: function() {
console.log('hello world');
}
}

// 使用
myModule.myMethod()

Module (模块) 模式

模块模式使用闭包封装’私有’状态和组织, 它提供了一直包装混合公有/私有方法和变量的方式, 防止其泄露至全局变量, 这为我们提供了一个屏蔽处理底层事件逻辑的整洁解决方案, 该模式除了返回一个对象而不是一个函数之外, 非常类似于一个立即调用的函数表达式

我们可以使用这种模式实现简单的购物车

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var basketModule = (function(){

// 私有属性和方法
var basket = [];
function dosomethingPrivate() {
...
}

// 返回一个暴露出的公有对象
return {
// 添加item到购物车
addItem: function(value) {
basket.push(value);
},

// 获取购物车里的item个数
getItemCount: function() {
return basket.length;
},

// 私有函数的公有化别名
doSomething: dosomethingPrivate,

// ...其他函数. 操作等
}
})()

// 使用: basketModule返回一个拥有公有API的对象
baskModule.addItem({
item: 'bread',
price: 4
})
console.log(baskModule.getItemCount) /// 输出1

注意: 这里的basket并没有暴露出来, 只存在于basketModule的闭包作用域里面, 外界无法使用

优缺点

  • 优点: 可以使脚本语法更加一致, 很容易指出哪些函数和变量可以被公开访问, 提高可读性
  • 缺点: 如果一个私有函数引用一个公有函数, 在需要补丁时, 公有函数是不能被覆盖的. 引用私有变量的公有对象成员也遵守无补丁规则

Singleton (单例)

  • 定义: 单例模式就是在实例不存在的情况下, 可以通过一个方法创建一个类来实现创建类的新实例, 如果实例已经存在, 他会简单返回该对象的引用
  • 在Javascript中, Singleton充当共享资源命名空间, 从全局命名空间中隔离出代码实现, 从而为函数提供单一访问点
  • 适用性:
    • 当类只能有一个实例而且用户可以从一个众所周知的访问点访问它时
    • 该唯一的实例是通过子类化可扩展的, 并且客户应该无需更改代码就能使用一个扩展的实例时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var Singletontester = (function(){

function Singleton(option) {
option = option || {}

// 设置一些属性
this.name = 'Singletontester';
this.pointX = option.pointX || 6;
this.pointY = option.pointY || 6;
}

// 实例持有者
var instance;

// 静态变量和方法的模拟
var _static = {
name: 'Carol',
// 获取实例的方法, 返回Singleton对象的singleton实例
getInstance: function(options) {
if(instance === undefined) {
instance = new Singleton(options);
}
return instance;
}
};
return _static;
})()

// 使用
var test = Singletontester.getInstance({
pointX: 5
})
console.log(test.pointX) // 输出