發布訂閱模式與簡單實現
本文寫於 2020 年 9 月 22 日
觀察者模式(Observer Pattern)是一種設計模式,也可以叫做「發布-訂閱模式」。
等等,其實我也不清楚,通常你在網上會看到上面這一段話,但我在 Angular 文檔中讀到谷歌的說法是這樣的:觀察者模式和發布/訂閱模式非常相似(但不完全一樣)。
好吧,我們姑且就直說發布訂閱模式吧。
發布訂閱模式就像你訂了一本雜志,每當雜志新刊發布的時候,所有訂閱過雜志的人都會收到新刊一樣。
觀察者模式定義了對象之間一種 一對多 的依賴關系,每當該對象的狀態發生改變的時候,所依賴於它的其他對象都將得到通知。
在 JavaScript 中,我們會經常接觸到一個很類似的東西——事件。
事件與觀察者模式
我們先為一個 #app 元素添加兩個點擊事件:
document.querySelector('#app').addEventListener('click', () => {
// 事件 1
});
document.querySelector('#app').addEventListener('click', () => {
// 事件 2
});
這個時候,我們點擊 #app 元素或者使用 document.querySelector('#app').click()
或者 document.querySelector('#app').dispatchEvent('click')
來觸發時,這兩個事件都會被觸發——是不是類似與很多對象依賴於一個對象,該對象改變后所有依賴者都會被觸發。
自定義「發布-訂閱」
我們來自定義一個事件來試試。
首先我們需要一個數組儲存所有的觸發函數,這樣我們就可以對其進行遍歷,后觸發每一個事件。
const children = [];
我們還需要一個方法,來對 children 數組進行 push
。
const listen = (fn) => {
children.push(fn);
}
我們再增加一個觸發函數 trigger
,讓它去遍歷並觸發我們的 children
const trigger = () => {
children.forEach((child) => {
child();
})
}
OK,接下來我們整合一下:
class eventHub {
children = {}
listen(key, fn) {
if(this.children[key]) {
this.children[key].push(fn);
} else {
this.children[key] = [fn]
}
}
trigger(key) {
this.children[key]?.forEach((child) => {
child();
});
}
remove(key, fn) {
if(this.children[key]) {
const children =this.children[key]
const target = children.indexOf(fn)
this.children[key] = [...children.slice(0, target), ...children.slice(target + 1)]
}
}
}
這里我們將數組換成了對象,並且為函數增加了參數,因為我們希望夠可以對多個事件進行監聽。
還添加了一個移除事件——所以我們最好使用具名函數,如果是匿名函數就無法移除監聽了!
有哪些例子?
例如一個電商網站的登陸系統,需要獲取用戶基礎信息后,獲取用戶的各項歷史記錄:
function onLogin() {
getUserInfo()
.then(() => {
return getUserHistory()
})
.then(() => {
getUserXXX()
})
}
那么一旦我們需要新增一個功能,就需要在這個長函數中增加新項。
如果使用了發布-訂閱模式,就可以非常簡單的寫上新的、獨立的事件監聽了。
(完)