最近在学习JavaScript设计模式,学习过之后感觉很有收获,现把自己的学习内容总结如下:
模式:是一种可服用的解决方案,可用于解决软件设计中遇到的常见问题。
设计模式:有三大好处,模式是已经验证的解决方案;模式很容易被复用;模式富有表达力。
本篇主要介绍8种设计模式,其余模式将在后续博文中继续介绍。
Constructor是一种在内存已分配给对象的情况下,用于初始化新建对象的特殊方法
newObject.someKey = "Hello Vicky";
newObject[someKey] = "Hello Vicky";
Object.defineProperty(newObject,"someKey",{
value : "Hello Vicky",
writable : true,
enumerable: true
})
Object.defineProperties(newObject,{
"someKey":{
value : "Hello Vicky",
writable : true,
enumerable: true
},
"anotherKey":{
//···
}
})
在构造器内,this关键字引用新创建的对象
//Constuctor模式很简单,只举个简单的小例子
function Car (model,name) {
this.model = model;
this.name = name;
this.toString = function () {
return this.model + "is named" + this.name;
};
}
var car = new Car("car","BMW");
console.log(car.toString());//car is named BMW.
上述代码的缺点在于所有实例共享toString()方法,可以用原型来改写一下,就变成了 构造函数-原型组合。可参考之前博文,不再赘述。
模块是任何强大的应用程序架构中不可或缺的一部分,它通常能够帮助我们清晰的分离和组织项目中的代码单元。
Module模式在某种程度上可以说是基于对象字面量的,该模式返回一个对象,很类似于一个立即调用的函数表达式
Module模式:用于进一步模拟类的概念,通过这种方式,能够使一个单独的对象拥有共有/私有方法和变量,从而屏蔽来自全局作用域的特殊部分。
Module模式使用闭包封装“私有”状态和组织。通过该模式,只需返回一个公有API,其他的一切都维持在私有闭包中
用Module模式来实现一个购物车,模块本身包含在basketModule的全局变量中。模块中的basket数组是私有的,因此在程序其他地方无法访问到。具体代码如下:
//Module实现购物车
var basketModule = (function () {
//私有
var basket = [];//存放商品
function doSomethingPrivate () {
// do something
}
//返回一个公有的对象
return {
//添加item到购物车
addItem: function (item) {
basket.push(item);
},
//获取购物车里item的数量
getItemCount: function () {
return basket.length;
},
//获取购物车里item的总价格
getTotalMoney: function () {
var itemCount = this.getItemCount();
var total = 0;
for(var i = 0; i < itemCount; i ++) {
total += basket[i].price;
}
return total;
}
};
}) ();
//basketModule 返回了一个拥有公用API的对象
basketModule.addItem({
item : "bread",
price: 0.5
});
basketModule.addItem({
item : "egg",
price: 1.0
});
var totalMoney = basketModule.getTotalMoney();
console.log(totalMoney);//1.5
优点:有一定的封装思想,支持私有数据
缺点:由于访问公有和私有成员的方式不同,当想改变可见性时,必须要修改每一个曾经使用过该成员的地方。(这只是其中一个缺点,但是其他缺点我还没有完全理解,欢迎交流补充)
揭示模块模式:能够在私有范围内简单定义所有的函数和方法,并返回一个匿名对象,它拥有指向私有函数的引用。
优点:很容易在模块底部看出哪些函数和变量可以被公开访问,改善了可读性
缺点:脆弱,谨慎使用
Singleton单例模式: 限制了类的实例化次数只能一次,在实例不存在的情况下,通过调用方法创建一个类来实现新实例的创建,若实例已经存在,则返回该对象的引用
在JavaScript中,Singleton充当共享资源命名空间,从全局命名空间中隔离出代码实现,从而为函数提供单一访问点
var singletonTest = SingletonTester.getInstance({
name : "vicky",
pointX: 20
});
console.log(singletonTest);
适用性: 当类只能有一个实例而客户可以从一个众所周知的访问点访问它时。
Prototype模式:我们可以认为其实基于原型继承的模式,可以在其中创建对象,作为其他对象的原型。
该模式与之前博文中继承里的原型继承类似,不再赘述。【JavaScript难点1】继承
Command模式:旨在将方法调用/请求或操作封装到单一对象中,从而根据我们不的请求对客户进行参数化和传递可供执行的方法调用。个人理解,就是多了一个execute方法,能够按需调用。
execute()使得我们可以调用任意方法,并且可以传递参数。
Facade:尽管一个实现可能支持具有广泛行为的方法,但是却只有一个“外观”能够供公众使用。
例如,跨浏览器的添加事件处理程序,
addHandler: function (element,type,handler) {
if (element.addEventListener) {
element.addEventListener(type,handler,false);
} else if (element.attachEvent) {
element.attachEvent("on" + tye,handler);
} else {
element["on" + tyle] = handler;
}
}
我们在调用addHandler时,实际上会触发一系列的私有行为,但是我们并不知道,我们只知道 用这个方法就可以跨浏览器的添加事件处理程序,这其实就是一种Facade模式。
Factory模式:不显式的要求使用一个构造函数,而是提供一个通用的接口来创建对iang,我们可以指定我们所希望创建的工厂对象的类型。
//工厂模式
//工厂模式可以提供一个通用的接口来创建对象,它不显式的要求使用一种构造函数
//定义构造函数
function Car (options) {
this.doors = options.doors || 4;
this.state = options.state || "brand new";
this.color = options.color || "red";
}
function Truck (options) {
this.state = options.state || "used";
this.wheelSize = options.wheelSize || "large";
this.color = options.color || "blue";
}
//定义工厂
function VehicleFactory () {}
VehicleFactory.prototype.vehicleClass = Car;
VehicleFactory.prototype.createVehicle = function (options) {
if (options.vehicleType === "Car") {
this.vehicleClass = Car;
} else {
this.vehicleClass = Truck;
}
return new this.vehicleClass(options);
};
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle({
vehicleType: "war",
color : "black",
doors : 6
});
console.log(car);
Mixin:可以轻松被一个子类或者一组子类继承功能的类。
目的是函数复用
我们可以把Mixin看作一种通过扩展来收集功能的方式。我们定义的每个新对象都有一个原型,可以从中继承更多属性。原型可以继承与其他对象的原型,并且,它可以为任一数量的对象实例定义属性。
var Car = function (settings) {
this.model = settings.model;
this.color = settings.color;
};
//Mixin
var Mixin = function () {};
Mixin.prototype = {
driveForward: function () {
console.log("drive forward");
},
driveBackward: function () {
console.log("drive backward");
}
};
//通过一个方法将现有对象扩展到另一个对象上
function extend (receivingClass,givingClass) {
for (var methodName in givingClass.prototype){
if(! receivingClass.prototype[methodName]){
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}
//使用
extend(Car,Mixin);
//创建一个Car
var myCar = new Car( {
model: "Ford",
color: "red"
});
myCar.driveForward();//drive forward
**优点:**Mixin有助于减少系统中的重复功能及添加函数复用
本篇博文只做了简单的介绍和梳理,相关代码已放在github上 JavaScriptDesignPatterns 。
另外 观察者模式和中介者模式有很多相似之处,将专门写博文进行学习。