發(fā)布-訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個(gè)對象的狀態(tài)發(fā)生改變的時(shí)候,所有依賴于它的對象都將得到通知。
舉個(gè)栗子
比如售樓中心,因?yàn)榉吭淳o張,小紅、小明、老王每天都要打電話到售樓前臺詢問今天是否有新樓盤,這樣售樓前臺每天就需要接收很大量的咨詢。
發(fā)布-訂閱模式就是,售樓前臺MM將每個(gè)人的電話都記錄下來,當(dāng)有新樓盤的時(shí)候,就遍歷一遍這些電話號碼,依次打電話通知。其中售樓前臺是發(fā)布者,小紅、小明、老王是訂閱者。
優(yōu)點(diǎn):
- 在合適的時(shí)間發(fā)布者會通知訂閱者。這說明發(fā)布-訂閱模式可以廣泛應(yīng)用于異步編程中,可以替代回掉函數(shù)的方式,無需過多關(guān)心異步運(yùn)行期間的內(nèi)部狀態(tài),只需要訂閱感興趣的事件發(fā)生點(diǎn)。
- 減少發(fā)布者和訂閱者之間的耦合,發(fā)布者只記錄一個(gè)訂閱者的電話,并不關(guān)心訂閱者是女人還是猴子。這個(gè)優(yōu)點(diǎn)說明可以訂閱-發(fā)布模式可以取代對象之間硬編碼的通知機(jī)制,不需要再顯式的調(diào)用另一個(gè)對象的某個(gè)接口,這讓對象之間松耦合的聯(lián)系在一起,當(dāng)新訂閱者出現(xiàn)的時(shí)候,發(fā)布者代碼不需要作任何修改,同理,當(dāng)發(fā)布者需求改變時(shí)也不會影響到之前的訂閱者。

發(fā)布訂閱模式.png
典型應(yīng)用場景
- 時(shí)間解耦:應(yīng)用于異步編程
- 對象解耦:接口之間調(diào)用不再顯示的進(jìn)行
發(fā)布-訂閱模式通用實(shí)現(xiàn)
var event = {
clientList: [], //緩存列表,存放訂閱者的回掉函數(shù)
// 增加訂閱者
addListen: function (key, fn) { // key是訂閱的消息類型,fn是回調(diào)函數(shù)
if (!this.clientList[key]) {
this.clientList[key] = []; // 如果還沒有訂閱過此類消息,給該類消息創(chuàng)建一個(gè)緩存列表
}
this.clientList[key].push(fn); // 訂閱的消息添加進(jìn)消息緩存列表
},
// 發(fā)布消息
emit: function () {
var key = Array.prototype.shift.call(arguments), // 取出消息類型
fns = this.clientList[key]; // 取出該類型的消息對應(yīng)的回調(diào)函數(shù)組成的數(shù)組
if(!fns || fns.length === 0) {
return false;
}
for (var i=0,fn; fn = fns[i++]) {
fn.apply(this, arguments); //arguments是發(fā)布消息時(shí)附送的參數(shù)
}
}
}
再定義一個(gè) installEvent 函數(shù),這個(gè)函數(shù)可以給所有的對象都動態(tài)安裝發(fā)布—訂閱功能:
var installEvent = function( obj ){ for ( var i in event ){ 給obj對象安裝發(fā)布-定義模式
obj[ i ] = event[ i ]; }
};
給售樓處對象增加發(fā)布-訂閱功能:
var salesOffice = {};
installEvent(salesOffice); // 添加訂閱-發(fā)布完畢
// 對象小明訂閱消息
salesOffice.addListen('square88', function(price) {
console.log( '價(jià)格= ' + price );// 回調(diào)函數(shù)
});
// 對象小紅訂閱消息
salesOffice.addListen('square100', function(price) {
console.log( '價(jià)格= ' + price );// 回調(diào)函數(shù)
});
// 發(fā)布消息
salesOffices.emit( 'square88', 2000000 ); // 輸出2000000
salesOffices.emit( 'square100', 3000000 ); // 3000000