1. 单例模式
定义:一个特定类 仅有一个实例
应用场景:
-
实现方式
每个对象都是一个单体,因为他们的引用位置不一样;
-
使用
new
操作符将首次生成的对象存储在全局变量中。缺点:全局变量易被覆盖。可以使用闭包来存储这个变量,参见 。
在构造函数的属性中存储该实例。缺点:构造函数的属性也容易被修改。
function Universe() { if (typeof Universe.instance === "object") { return Universe.instance } this.start_time = 0; this.bang = "Big"; Universe.instance = this; } var uni1 = new Universe(); var uni2 = new Universe(); console.log(uni1 === uni2); // true
- 重写构造函数,使用闭包来储存这个实例。缺点:
//缺点: 这种方式会丢掉初始实例与重写构造函数之间的关系,因此后面再向重写的构造函数原型中添加属性时,并不会添加到初始实例中去。 function Universe() { var instance = this; this.start_time = 0; this.bang = "Big"; Universe = function Universe() { return instance; } } Universe.prototype.nothing = true; var uni1 = new Universe(); Universe.prototype.everything = true; var uni2 = new Universe(); console.log(uni1 === uni2); //true console.log(uni1.nothing); //true console.log(uni2.nothing); //true console.log(uni1.everything); //undefined console.log(uni2.everything); //undefined console.log(uni1.constructor === Universe); //false
因此,我们需要先重写构造函数、保留原型属性、生成实例,返回实例。
function Universe() { var instance; Universe = function () { return instance; } Universe.prototype = this; instance = new Universe(); instance.constructor = Universe; this.start_time = 0; this.bang = "big"; return instance; } Universe.prototype.nothing = true; var uni1 = new Universe(); Universe.prototype.everything = true; var uni2 = new Universe(); console.log(uni1 === uni2); //true console.log(uni1.nothing); //true console.log(uni2.nothing); //true console.log(uni1.everything); //true console.log(uni2.everything); //true console.log(uni1.constructor === Universe); //true
- 还有一种就是使用即时函数
var Universe; (function () { var instance; Universe = function () { if (instance) { return instance; } instance = this; this.start_time = 0; this.bang = "big"; } })(); Universe.prototype.nothing = true; var uni1 = new Universe(); Universe.prototype.everything = true; var uni2 = new Universe(); console.log(uni1 === uni2); //true console.log(uni1.nothing); //true console.log(uni2.nothing); //true console.log(uni1.everything); //true console.log(uni2.everything); //true console.log(uni1.constructor === Universe); //true
2. 工厂模式
定义: 提供创建相似对象的可重用方法。在不确定的情况下,提前提供一种创建某类的方法。总-分模式。
应用场景:一个
CarMaker
的构造函数,提供创建不同类型汽车的factory
方法。实现方式。优点:保护全局命名空间免受污染,能很快地找到相应的构造函数。
function CarMaker() {} CarMaker.prototype.getPrice = function () { console.log("Price:", this.price); } CarMaker.factory = function (type) { var newcar; //如果构造函数不存在,报错 if (typeof CarMaker[type] !== "function") { throw { name: "Error", message: type + " dosen't exist" } } //原型继承父类,且只继承一次 if (typeof CarMaker[type].prototype.getPrice !== "function") { CarMaker[type].prototype = new CarMaker(); } newcar = new CarMaker[type](); return newcar; } CarMaker.SUV = function () { this.price = 100; } CarMaker.Compat = function () { this.price = 50; } var a = CarMaker.factory("SUV"); var b = CarMaker.factory("Compat"); a.getPrice(); //Price: 100 b.getPrice(); //Price: 100
3. 迭代器模式
定义:按顺序访问一个聚合对象中各个元素。通常包含
next(获取下一个元素), hasNext(是否到末尾),rewind(重置初始位置), current(当前元素)
集中方法。应用场景:需要按照顺序,以不同的间隔获取集合中的元素。
实现方法:
function iterator (data = [], gap = 1) { var index = 0, //记录当前位置 length = data.length; //数据总长度 return { next() { var element; if (!this.hasNext()) { return null; } element = data[index]; index += gap; return element; }, hasNext() { return index < length; }, rewind() { index = 0; }, current() { return data[index]; } } } var arrIterator = iterator([1, 2, 3, 4, 5]); while(arrIterator.hasNext()) { console.log(arrIterator.next()); } arrIterator.rewind(); console.log(arrIterator.current());
4. 装饰者模式
定义: 在运行时动态添加附加功能到对象中。
应用场景:购买某种东西时,随着添加不同的装备,价格也不一样。最后需要根据添加的设备计算总价。
实现方法:要注意的是,各装饰器需要提供原型方法的接口。
function Sale(price = 100) { this.price = price; this.decorators_list = []; } //装饰器 Sale.decorators = {}; Sale.decorators.pack = { getPrice(price) { return price + 20; } } Sale.decorators.mail = { getPrice(price) { return price + 10; } } Sale.prototype.decorate = function (decorator) { this.decorators_list.push(decorator); } Sale.prototype.getPrice = function () { let price = this.price; //调用各装饰器的接口 for (let i = 0, len = this.decorators_list.length; i < len; i++) { name = this.decorators_list[i]; price = Sale.decorators[name].getPrice(price); } return price; } var sale = new Sale(); sale.decorate('pack'); sale.decorate('mail'); console.log(sale.getPrice()); //130
5. 策略者模式
定义:在运行时可以选择算法。
应用场景:表单验证。
实现方式:一个含有
config(配置验证规则), types(验证类型), validate(验证方法), message(结果消息数组), hasErrors(最后结构)
几个属性的validator
对象。
6. 外观模式
定义:提供一个高级接口,调用经常需要同时使用的几个基础方法。
应用场景:将
stopPropagation
与peventDefault
包装在stop
这个高级接口中。
饭堂的炒菜师傅不会因为你预定了一份烧鸭和一份白菜就把这两样菜炒在一个锅里。他更愿意给你提供一个烧鸭饭套餐。同样在程序设计中,我们需要保证函数或者对象尽可能的处在一个合理粒度,毕竟不是每个人喜欢吃烧鸭的同时又刚好喜欢吃白菜。
外观模式还有一个好处是可以对用户隐藏真正的实现细节,用户只关心最高层的接口。比如在烧鸭饭套餐的故事中,你并不关心师傅是先做烧鸭还是先炒白菜,你也不关心那只鸭子是在哪里成长的。
7. 代理模式
定义:介于对象和对象的客户端之间,对对象本体起保护作用,使本体对象做尽可能少的工作。目的是解决性能问题。
-
应用场景:
初始化本体对象消耗非常大,但是在初始化该对象的之后并未使用。此时可使用代理模式,接收初始化请求,返回ok,等真正用到本体对象时再初始化。
发起Http请求会消耗性能,最好的方法就是使用代理合并Http请求,比如将50ms内的发起的请求合并为一个请求。
为了减少性能消耗,还可给代理设置一个缓存,缓存结果。
fragment应该就算一个代理模式吧,缓存了对DOM的操作。
实现方法:对象客户端调用代理提供的方法,代理对客户端的请求进行处理后调用对象本体方法。
8. 中介者模式
定义:一个程序中有很多对象,这些对象之间会相互通信。但是如果修改其中一个对象,则必然会牵涉到其他和他通信的对象,造成紧耦合。中介者模式就是提出一个中间对象来实现对象间的松耦合通信。通信的对象不用知道对方是谁,只要有变动就通知给这个中介者,中介者知道所有的对象并且负责联系相应的对象做出处理。
应用场景:MVC中的Controller
实现方法:
9. 观察者模式
定义:该模式的目的也是为了形成松耦合。对象之间不直接调用彼此的方法,而是采取订阅的模式,订阅者订阅发布者的事件,并向发布者提供一个事件发生时调用的方法。中介者模式中的mediator需要知道所有其他对象,而该模式并不需要知道,只要订阅事件即可。
应用场景:DOM中的事件。
实现方法:
发布者需要:订阅者的列表{type: []}
,订阅方法, 取消订阅,发布消息几种方法。