发布订阅模式的定义:它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
。
发布订阅模式在JS中最常见的就是DOM的事件绑定与触发:
//todo 注册点击事件 btn.addEventListener("click", function (event) { console.log("点击事件触发了"); }); //todo 执行点击事件 btn.click();
这两句代码就是该模式的核心:注册了点击事件,在某个特定时刻(这里是按钮点击)执行注册的事件。
在vue里的事件注册也是用的该模式,这里是vue里绑定事件的模仿:
class VueEvent { constructor() { this.callbakcs = Object.create(null); } on(type, cb) { if (!(type in this.callbakcs)) { this.callbakcs[type] = []; } this.callbakcs[type].push(cb); return this; } off(type, fn) { if (!(type && fn)) { this.callbakcs = Object.create(null); } else if (type && !fn) { delete this.callbakcs[type]; } else { const thisTypeCBs = this.callbakcs[type]; for (const key in thisTypeCBs) { if (fn == thisTypeCBs[key] || fn == thisTypeCBs[key].fn) { thisTypeCBs.splice(key, 1); } } } return this; } once(type, cb) { const _this = this; function innerOnce(...arg) { cb(...arg); _this.off(type, innerOnce); } innerOnce.fn = cb; this.on(type, innerOnce); return this; } emit(type, ...arg) { if (type in this.callbakcs) { const runs = [...this.callbakcs[type]]; //! 深复制 下面的循环里有可能会删除数组元素 for (const cb of runs) { cb(...arg); } } } }
就本质来看,该模式在JS里的实现仍然是依靠JS的动态语言特性:能随意随时在对象中添加属性,方法;函数也是对象能被传递的特点。
在策略模式中的最后个例子里我用了一个包装类来收集每个元素运用的策略方法,这里与发布订阅模式很像当仍然有根本上的区别:
其实就策略模式最基本的实现上是不需要一个收集策略的类的,从这方面看它和观察者模式的相似点就只有“都有一个保存函数的缓存,在某个时候会被执行”。