设计模式的定义:在面向对象软件设计过程当中针对特定问题的简洁而优雅的解决方案javascript
固然咱们能够用一个通俗的说法:设计模式是解决某个特定场景下对某种问题的解决方案。所以,当咱们遇到合适的场景时,咱们可能会条件反射同样天然而然想到符合这种场景的设计模式。java
好比,当系统中某个接口的结构已经没法知足咱们如今的业务需求,但又不能改动这个接口,由于可能原来的系统不少功能都依赖于这个接口,改动接口会牵扯到太多文件。所以应对这种场景,咱们能够很快地想到能够用适配器模式来解决这个问题。算法
下面介绍几种在JavaScript中常见的几种设计模式:设计模式
单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,若是存在则直接返回,若是不存在就建立了再返回,这就确保了一个类只有一个实例对象。app
适用场景:一个单一对象。好比:弹窗,不管点击多少次,弹窗只应该被建立一次。异步
class CreateUser {
constructor(name) {
this.name = name;
this.getName();
}
getName() {
return this.name;
}
}
// 代理实现单例模式
var ProxyMode = (function() {
var instance = null;
return function(name) {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
// 测试单体模式的实例
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
// 由于单体模式是只实例化一次,因此下面的实例是相等的
console.log(a === b); //true复制代码
策略模式的定义:定义一系列的算法,把他们一个个封装起来,而且使他们能够相互替换。函数
策略模式的目的就是将算法的使用算法的实现分离开来。测试
一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类(可变),策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context(不变),Context接受客户的请求,随后将请求委托给某一个策略类。要作到这一点,说明Context中要维持对某个策略对象的引用。ui
/*策略类*/
var levelOBJ = {
"A": function(money) {
return money * 4;
},
"B" : function(money) {
return money * 3;
},
"C" : function(money) {
return money * 2;
}
};
/*环境类*/
var calculateBouns =function(level,money) {
return levelOBJ[level](money);
};
console.log(calculateBouns('A',10000)); // 40000复制代码
代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。this
经常使用的虚拟代理形式:某一个花销很大的操做,能够经过虚拟代理的方式延迟到这种须要它的时候才去建立(例:使用虚拟代理实现图片懒加载)
图片懒加载的方式:先经过一张loading图占位,而后经过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面。
var imgFunc = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var proxyImage = (function() {
var img = new Image();
img.onload = function() {
imgFunc.setSrc(this.src);
}
return {
setSrc: function(src) {
imgFunc.setSrc('./loading,gif');
img.src = src;
}
}
})();
proxyImage.setSrc('./pic.png');复制代码
使用代理模式实现图片懒加载的优势还有符合单一职责原则。减小一个类或方法的粒度和耦合度。
中介者模式的定义:经过一个中介者对象,其余全部的相关对象都经过该中介者对象来通讯,而不是相互引用,当其中的一个对象发生改变时,只须要通知中介者对象便可。经过中介者模式能够解除对象与对象之间的紧耦合关系。
例如:现实生活中,航线上的飞机只须要和机场的塔台通讯就能肯定航线和飞行状态,而不须要和全部飞机通讯。同时塔台做为中介者,知道每架飞机的飞行状态,因此能够安排全部飞机的起降和航线安排。
中介者模式适用的场景:例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等等,都会触发change事件,那么能够经过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象便可。
var goods = { //手机库存
'red|32G': 3,
'red|64G': 1,
'blue|32G': 7,
'blue|32G': 6,
};
//中介者
var mediator = (function() {
var colorSelect = document.getElementById('colorSelect');
var memorySelect = document.getElementById('memorySelect');
var numSelect = document.getElementById('numSelect');
return {
changed: function(obj) {
switch(obj){
case colorSelect:
//TODO
break;
case memorySelect:
//TODO
break;
case numSelect:
//TODO
break;
}
}
}
})();
colorSelect.onchange = function() {
mediator.changed(this);
};
memorySelect.onchange = function() {
mediator.changed(this);
};
numSelect.onchange = function() {
mediator.changed(this);
};复制代码
装饰者模式的定义:在不改变对象自身的基础上,在程序运行期间给对象动态地添加方法。
例如:现有4种型号的自行车分别被定义成一个单独的类,若是给每辆自行车都加上前灯、尾灯、铃铛这3个配件,若是用类继承的方式,须要建立4*3=12个子类。但若是经过装饰者模式,只须要建立3个类。
装饰者模式适用的场景:原有方法维持不变,在原有方法上再挂载其余方法来知足现有需求;函数的解耦,将函数拆分红多个可复用的函数,再将拆分出来的函数挂载到某个函数上,实现相同的效果但加强了复用性。
例:用AOP装饰函数实现装饰者模式
Function.prototype.before = function(beforefn) {
var self = this; //保存原函数引用
return function(){ //返回包含了原函数和新函数的 '代理函数'
beforefn.apply(this, arguments); //执行新函数,修正this
return self.apply(this,arguments); //执行原函数
}
}
Function.prototype.after = function(afterfn) {
var self = this;
return function(){
var ret = self.apply(this,arguments);
afterfn.apply(this, arguments);
return ret;
}
}
var func = function() {
console.log('2');
}
//func1和func3为挂载函数
var func1 = function() {
console.log('1');
}
var func3 = function() {
console.log('3');
}
func = func.before(func1).after(func3);
func();复制代码