前阵子看了《JavaScript 设计模式与开发实际》,十分不错的一本书,整顿一些最罕用的设计模式,当前再补充一些其它的。
单例模式
保障一个类仅有一个实例,并且提供一个拜访它的全局拜访点。
实现单例模式
-
利用构造函数的实例属性(不通明)
let CreateDiv = function (name) { this.name = name this.instance = null } CreateDiv.prototype.init = function () {let div = document.createElement( 'div') // .... } CreateDiv.creatInstance = function (name) {if (!this.instance) {this.instance = new CreateDiv(name) } return this.instance } let lee1 = CreateDiv.creatInstance('lee1') let lee2 = CreateDiv.creatInstance('lee2') lee1 === lee2 // true
-
利用闭包(不通明)
let CreateDiv = function (name) {this.name = name} CreateDiv.prototype.init = function () {let div = document.createElement( 'div') // .... } CreateDiv.creatInstance = (function () { // 立刻执行函数 let instance = null return function (name) {if (!instance) {instance = new CreateDiv(name) } return instance } })() let lee1 = CreateDiv.creatInstance('lee1') let lee2 = CreateDiv.creatInstance('lee2') lee1 === lee2 // true
-
单例类(通明)
下面两种实现形式不通明,须要钻研代码的实现才晓得调 creatInstance 办法,这样不太好啊,我门用一般类来实现
let Singleton = (function () { let instance = null let CreateDiv = function (name) {if (instance) return instance this.name = name this.init() // 创立一个 div return instance = this // this 指向新创建的实例对象 } CreateDiv.prototype.init = function () {let div = document.createElement( 'div') // .... } return CreateDiv })() let lee1 = new Singleton('lee1') let lee2 = new Singleton('lee2') lee1 === lee2 // true
-
代理类(代理模式 – 通明)
// 有一咱们要创立 100 个 div 怎么办 恐怕只能批改 CreateDiv 了吧 // 代理类只负责单例模式,不影响原来的类,而且也很通明 let CreateDiv = function (name) { this.name = name this.init()} CreateDiv.prototype.init = function () {let div = document.createElement( 'div') // .... } <!-- 代理类 负责单例模式 不影响 --> let ProxyCreatInstance = (function () { let instance = null return function (name) {if (!instance) {instance = new CreateDiv(name) } return instance // 返回了实例 } })() let lee1 = new ProxyCreatInstance('lee1') // 间接 new 很通明,创立多个还用 CreateDiv 类 let lee2 = new ProxyCreatInstance('lee2') lee1 === lee2 // true // 这里波及到了 new 操作符原理,构造函数如果返回了援用类型,会笼罩掉产生的对象 function mockNew (func, params) {let obj = {} obj.__proto__ = func.prototype let res = func.call(obj, params) return res instanceof Object ? res : obj }
-
js 惰性单例(下面的都混入了传统面向对象语言的特点,没有利用 js 的特点)
// js 嘛 函数是一等公民,先写个通用的单例函数 function getSingle (fn) { let instance = null return function () {return instance || (instance = fn.call(this, arguments)) // fn 有返回 这样写更简洁 } } let CreateDiv = function (name) { this.name = name let div = document.createElement('div') div.setAttribute('id', name[0]) return div } let singleton = getSingle(CreateDiv) let lee1 = singleton('lee1') let lee2 = singleton('lee2') lee1 === lee2 // true button.addEventListener('click', function () {let lee = singleton('lee') } , false); <!-- 惰性单例就是指只在须要的时候才创立 比方我点击按钮后创立 -->
小结:不通明模式须要理解代码,不够敌对,基于传统的面向对象,咱们能够通过代理类来实现单例模式的透明化,创建对象和治理单例的职责被散布在两个不同的办法中。惰性单例技术就是指在适合的时候才创建对象。
代理模式
代理模式是为一个对象提供一个代用品或占位符,以便管制对它的拜访。当咱们不不便间接拜访对象本体的时候,通过代理去拜访它。
比方咱们下面单例模式中的代理类 ProxyCreatInstance,咱们如果不必代理,就像第三种单例类一样,代码耦合在一起,如果咱们想创立 100 个 div,只能去更改 CreateDiv 的外部,因为你 new 100 单例模式下个它也只有一个。用了代理模式后,当你须要用单例的时候走代理类就 ok 了,不影响原来的类。
小王要给小芳送花,他不善言辞,送花失败率高,他让小芳的敌人小明替他去送,小明挑了一个小芳开心的日子去送花,一下车替小王解决了终身大事。。。小明这个代理能够监听小芳的情绪,在她情绪好的时候再去送,这就是代理模式的作用。
function Flower () {}
let xiaoWang = {sendFlower: function (target) {let flower = new Flower()
target.receiveFlower(flower)
}
}
let xiaoMing = {receiveFlower: function (flower) {xiaoFang.listenGoodMood(function () {xiaoFang.receiveFlower(flower)
}
})
}
let xiaoFang = {receiveFlower: function (flower) {console.log('收到花')
},
listenGoodMood: function (fn) {setTimeout(fn, 10000)
}
}
xiaoWang.sendFlower(xiaoMing)
策略模式
定义一系列的算法,将不变的局部和变动的局部隔开,理论就是将算法的应用和实现分离出来,算法的应用形式是不变的
举个栗子
<!-- 年初核算绩效 -->
A 级 奖金为工资 *3
B 级 奖金为工资 *2
C 级 奖金为工资 *1
D 级 奖金为 0
function calculateBonus (performanceLevel, salary){if ( performanceLevel === 'A'){return salary * 3;}
if (performanceLevel === 'B'){return salary * 2;}
if (performanceLevel === 'C'){return salary * 1;}
};
calculateBonus('A', 10000);
calculateBonus('B', 5000);
---------------------------------------------
<!-- 这是咱们的策略 -->
const strategies = {'A': function (salary) {return salary * 3;},
'B': function (salary) {return salary * 2},
'C': function (salary) {return salary * 1},
'D': function (salary) {return salary * 0}
}
<!-- 应用办法是不变的 -->
const calculateBonus = function (performanceLevel, salary) {return strategies[performanceLevel](salary)
}
calculateBonus('A', 10000);
calculateBonus('B', 5000);
公布订阅模式
它定义对象间的一种一对多的依赖关系,当一个对象的状 态产生扭转时,所有依赖于它的对象都将失去告诉。例如你订阅了一些情报,情报中心方才公布了一些新音讯,如果外面有你订阅的情报时,就会告诉到你。公布订阅的益处是公布和订阅是齐全解耦的,它们通过音讯类型关联。
像 js 中的 addEventListener、vue 中的 bus 通信都是用的公布订阅模式。
button.addEventListener("click", function () {}, false);
// html
<button onclick="handleOn('m1')"> 订阅 - 放学啦 </button>
<button onclick="handleOn('m2')"> 订阅 - 老师来了 </button>
<button onclick="handleOn('m3')"> 订阅 - 打一把王者 </button>
<button onclick="handleSub('m1')"> 公布 - 放学啦 </button>
<button onclick="handleSub('m2')"> 公布 - 老师来了 </button>
<button onclick="handleSub('m3')"> 公布 - 打一把王者 </button>
// js
/**
* 公布订阅模式 公布订阅互不影响,通过音讯类型关联
*/
var Observe = function () {
// 定义音讯队列
this._msgQueue = {}}
Observe.prototype = {
// 音讯订阅
subscribe: function (type, fn, msg) {if (this._msgQueue[type] === void (0)) {this._msgQueue[type] = [fn]
} else {this._msgQueue[type].push(fn)
}
alert(msg)
},
// 公布
publish: function (type, args) {console.log(this._msgQueue[type])
if (!this._msgQueue[type]) return
let params = {
type: type,
args: args || {}}
let i = 0, len = this._msgQueue[type].length;
for (; i < len; i++) {this._msgQueue[type][i].call(this, params)
}
},
// 移除音讯订阅
off: function (type, fn) {if (this._msgQueue[type] instanceof Array) {let i = 0, len = this._msgQueue[type].length;
for (; i < len; i++) {if (this._msgQueue[type][i] === fn) {this._msgQueue[type].splice(i, 1)
return
}
}
}
}
}
// -----------------------------------
var Ming = new Observe()
function handleOn(type) {console.log('type', type);
switch (type) {
case 'm1':
console.log('m1');
let fun1 = function (params) {alert(params.args)
}
Ming.subscribe(type, fun1, '放学了 - 订阅胜利')
break;
case 'm2':
let fun2 = function (params) {alert(params.args)
}
Ming.subscribe(type, fun2, '老师来了 - 订阅胜利')
break;
case 'm3':
let fun3 = function (params) {alert(params.args)
}
Ming.subscribe(type, fun3, '打一把王者 - 订阅胜利')
break;
}
}
function handleSub(type) {switch (type) {
case 'm1':
Ming.publish(type, '放学了,快回家找妈妈')
break;
case 'm2':
let fun2 = function (params) {alert(params.args)
}
Ming.publish(type, '老师来了 - 快把手机藏起来')
break;
case 'm3':
let fun3 = function (params) {alert(params.args)
}
Ming.publish(type, '同学们请留神,这堂课咱们一起玩王者农药')
break;
}
}
中介者模式
中介者模式的作用就是解除对象与对象之间的紧耦合关系。减少一个中介者对象后,所有的相干对象都通过中介者对象来通信,而不是相互援用,所以当一个对象产生扭转时,只须要告诉中介者对象即可
毛病:零碎中会新增一个中介者对象,减少了对象之间交互的复杂性,中介者对象常常是微小的,有时候难以保护。