设计模式
设计模式类别
创建型设计模式
创建型设计模式专注于处理对象创建机制,以适合给定情况的方式来创建对象。创建对象的基本方法可能导致项目复杂性的增加,而这些模式意在通过控制创建过程来解决这些问题
属于这个类别的模式有:
Constructor
(构造器)、Factory
(工厂)、Abstract
(抽象)、Prototype
(原型)、Singleton
(单例)、Builder
(生成器)结构性设计模式
结构型模式与对象组合有关,通常可以用于找出不同对象之间建立关系的简单方法。这种模式有助于确保在系统某一部分发生变化时,系统的整个结构不需要同时改变。同时对于不适合因某一特定目的而改变的系统部分,这种模式也能够帮助它们完成重组
属于这个类别的模式有:
Decorator
(装饰者)、Facade
(外观)、Flyweight
(亨元)、Adapter
(适配器)、Proxy
(代理)行为设计模式
行为模式专注于改善或简化系统中不同对象之间的通信
行为模式包括:
Iterator
(迭代器)、Mediator
(中介者)、Observer
(观察者)、Visitor
(访问者)
JavaScript
设计模式
Constructor
(构造器)模式
在js
中创建新对象的两种常用方法
1. var newObject = {}
2. var newObject = new Object()
有四种方法可以将键值赋值给一个对象
1. “点”语法
2. 中括号语法
3. Object.defineProperty()
4. Object.defineProperties()
基本Constructor
(构造器)
在js
中不支持类的概念,但是它确实支持与对象一起用的特殊constructor
(构造器)函数。通过在构造器前面加new
关键字,告诉js
像使用构造器一样实例化一个新对象,并且对象成员有函数定义。
在构造器内,关键字this
引用新创建的对象
function Car(model,year,miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + this.year + this.miles;
}
}
//创建实例
var civic = new Car("Honda civic",2009,20000);
var mondeo = new Car("Ford mondeo",2010,5000);
带原型Constructor
(构造器)
在js
中有一个名为prototype
的属性,调用js
构造器创建一个对象以后,新对象就会具有构造器原型的所有属性,通过这种方式,可以创建多个Car
对象,并访问相同的属性,所以我们可以扩展原型实例如下:
function Car(model,year,miles) {
this.model = model;
this.year = year;
this.miles = miles;
}
Car.prototype.toString = function() {
return this.model + this.year + this.miles;
}
//创建实例
var civic = new Car("Honda civic",2009,20000);
var mondeo = new Car("Ford mondeo",2010,5000);
这种方法的好处就是不用每实例一个对象就创建一个toString
函数,节约了内存,放在prototype
中之后就能够在所有的Car
之间共享
Module
(模块)模式
模块是任何强大的应用程序不可或缺的一部分,它通常能够帮助我们清晰地分离和组织项目中的代码单元
在js
中,有几种用于实现模块的方法,包括
1. 对象字面量表示法
2. Module
模式
3. AMD
模式
4. CommonJS
模块
5. ECMAScript Harmony
模块
对象字面量
在对象字面量表示法中,一个对象被描述为一组包含在大括号中、以逗号分隔的name/value
对。对象内的名称可以是字符串或标识符,后面跟着一个冒号。对象中的最后一个name/value
对的后面不用加逗号,如果加逗号会导致出错。
var myObjectLiteral = {
varibaleKey:varibaleValue,
functionKey:function () {
}
};
对象字面量不需要使用new
运算符进行实例化,但不能用在一个语句的开头,因为开始的可能被解读为一个块的开始。在对象的外部,新成员可以使用如下赋值语句添加到对象字面量上:myModule.property = "someValue"
;
下面我们可以看到一个完整的实例,使用对象字面量表示法定义的模块:
//对象字面量表示法
var myModule = {
myProperty:"someValue",
myConfig:{
useCaching:true,
language:"en"
},
//基本方法
myMethod:function () {
console.log("asdasd");
},
myMethod2:function () {
console.log(this.myConfig.useCaching);
},
//重写myConfig属性
myMethod3:function (newConfig) {
if(typeof newConfig == "Object"){
this.myConfig = newConfig;
}
}
};
//对象调用
myModule.myMethod();
使用对象字面量有助于封装和组织代码
Module
模式
Module
模式最初被定义为一种在传统软件工程中卫类提供私有和公有封装的方法。
在js
中,Module
模式用于进一步模拟类的概念,通过这种方式,能够使一个单独的对象拥有公有/私有方法和变量,从而屏蔽来自全局作用域的特殊部分。产生的结果是:函数名与在页面上其他脚本定义的函数冲突的可能性降低
- 私有
Module
模式使用闭包封装“私有”状态和组织。它提供了一种包装混合公有/私有方法和变量的方式,防止其泄露至全局作用域,并与别的开发人员的接口发生冲突,通过该模式,只需返回一个公有的API,而其他的一切则都维持在私有闭包里。
这为我们提供了一个屏蔽处理底层事件逻辑的整洁解决方案,同时只暴露一个接口供应用程序的其他部分使用。该模式除了返回一个对象而不是一个函数之外,非常类似于一个立即调用的函数表达式。从技术上来说,我们不能称变量为公有或是私有,因此我们需要使用函数作用域来模拟这个概念。在Module模式内,由于闭包的存在,声明的变量和方法只在该模式的内部可用,但在返回对象上定义的变量和方法,则对外部使用者都是可用的。
示例:
var testModule = (function () {
var counter = 0;
return {
increment:function () {
return ++counter;
},
reset:function () {
counter = 0;
}
};
})();
testModule.increment();
1
testModule.increment();
2
在上面的这段代码中,运行时闭包中的函数会立即执行,然后将返回的对象赋给testModule
,调用它即可获取到闭包内部的counter
变量。代码中的其他部分无法直接读取increment()
与reset()
。counter
变量实际上是完全与全局作用域隔离的,因此它表现的就像是一个私有变量,它的存在被局限于模块的闭包内,因此唯一能够访问其作用域的代码就是这两个函数
使用Module
模式时,可能会觉得它可以用来定义一个简单的模板来入门使用,下面是一个包含命名空间、公有和私有变量的Module
模式
//Module模板
var myNamespaces = (function () {
//私有变量
var myPrivateVar = 0;
//私有方法
var myPrivateMethod = function (foo) {
console.log(foo);
};
//返回对象
return {
//公有变量
myPublicVar:"foo",
//公有方法
myPublicMethod:function (bar) {
myPublicVar++;
myPrivateMethod(bar);
}
}
})();
来看另一个实例,我们可以看到一个使用这种模式实现的购物车。模块本身是完全自包含在一个被称为basketModule
的全局变量中。模块中的basket
数组时私有的,因此应用程序的其他部分都无法直接读取它,它只与模块的闭包一起存在,所以能够访问它的方法都是那个能够访问其作用域的方法:
var basketModule = (function () {
//私有变量
var basket = [];
function doSomethingPrivate() {}
function doSomethingElsePrivate() {}
//公有变量
return {
addItem:function (values) {
basket.push(values);
},
getItemCount:function () {
return basket.length;
},
doSomething:doSomethingPrivate,
getTotal:function () {
var itemCount = this.getItemCount(),
total = 0;
while(itemCount--){
total += basket[itemCount].price
}
return total;
}
}
})();
basketModule.addItem({
item:"bread",
price:0.5
});
basketModule.addItem({
item:"bread",
price:0.5
});
console.log(basketModule.getItemCount())
VM2376:1 2
请注意上面的basket
模块中的作用域函数是如何包裹在所有函数的周围,然后调用并立即存储返回值,这有许多的优点,包括:
1. 只有我们的模块内部才能享有拥有私有函数的自由。因为它们不会暴露在页面的其他部分,只会暴露于我们输出的API
2. 鉴于函数往往已声明并命名,在试图找到有哪些函数抛出异常时,这使得在调试器中显示调用堆栈变得很容易
Module
模式变化
- 引入混入
模式的这种变化演示了全局变量(如jQuery、Underscore
)如何作为参数传递给模块的匿名函数。这允许我们引入它们,并按照我们所希望的为它们取个本地名
var myModule = (function (jQ,_) {
function privateMethod1() {
jQ(".container").html("reset");
}
function privateMethod2() {
cosole.log(_.min([10,200,30,23]));
}
return {
publicMethod:function () {
privateMethod2();
}
}
})(jQuery,_);
myModule.publicMethod();
2.引出
var myNamespaces = (function () {
var module = {};
//私有变量
var myPrivateVar = 0;
//私有方法
var myPrivateMethod = function (foo) {
console.log(foo);
};
module.myPublicVar = "foo";
module.myPublicMethod = function () {
myPrivateVar++;
myPrivateMethod("123");
}
//返回对象
return module;
})();
摘自:《JavaScript设计模式》