發布訂閱模式(觀察者模式)
發布訂閱模式的定義:它定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都將得到通知
。
發布訂閱模式在JS中最常見的就是DOM的事件綁定與觸發:
//todo 注冊點擊事件
btn.addEventListener("click", function (event) {
console.log("點擊事件觸發了");
});
//todo 執行點擊事件
btn.click();
這兩句代碼就是該模式的核心:注冊了點擊事件,在某個特定時刻(這里是按鈕點擊)執行注冊的事件。
在vue的事件綁定里用到了
在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的動態語言特性:能隨意隨時在對象中添加屬性,方法;函數也是對象能被傳遞的特點。
與策略模式例子里的區別
在策略模式中的最后個例子里我用了一個包裝類來收集每個元素運用的策略方法,這里與發布訂閱模式很像當仍然有根本上的區別:
- 策略模式不提供策略的增刪,而觀察者模式則提供了監聽的真假與移除;
- 策略模式需要知道策略方法接收的參數只能通過查看策略類,而觀察者模式的監聽方法是用戶自定義的;
其實就策略模式最基本的實現上是不需要一個收集策略的類的,從這方面看它和觀察者模式的相似點就只有“都有一個保存函數的緩存,在某個時候會被執行”。