生活中的發(fā)布訂閱
淘寶賣家屬于發(fā)布者。顧客小紅等一群吃瓜群眾是訂閱者。群眾訂閱淘寶賣家的店鋪,賣家有新消息就會(huì)通知所有人。
發(fā)布訂閱模式的優(yōu)點(diǎn)
支持簡(jiǎn)單的廣播通信,當(dāng)對(duì)象狀態(tài)發(fā)生改變時(shí),會(huì)自動(dòng)通知已經(jīng)訂閱過(guò)的對(duì)象。
比如上面的列子,小明,小紅不需要天天逛淘寶網(wǎng)看鞋子到了沒(méi)有,在合適的時(shí)間點(diǎn),發(fā)布者(賣家)來(lái)貨了的時(shí)候,會(huì)通知該訂閱者(小紅,小明等人)。發(fā)布者與訂閱者耦合性降低,發(fā)布者只管發(fā)布一條消息出去,它不關(guān)心這條消息如何被訂閱者使用,同時(shí),訂閱者只監(jiān)聽(tīng)發(fā)布者的事件名,只要發(fā)布者的事件名不變,它不管發(fā)布者如何改變;同理賣家(發(fā)布者)它只需要將鞋子來(lái)貨的這件事告訴訂閱者(買家),他不管買家到底買還是不買,還是買其他賣家的。只要鞋子到貨了就通知訂閱者即可。
簡(jiǎn)易版發(fā)布訂閱代碼
const shoeObj = {};// 定義發(fā)布者
shoeObj.list = {};// 緩存列表 存放訂閱者回調(diào)函數(shù)
// 增加訂閱者
shoeObj.listen = function listen(key, fn) {
if (Array.isArray(shoeObj.list[key])) {
shoeObj.list[key].push(fn);
} else {
shoeObj.list[key] = [fn];
}
};
// 發(fā)布消息
shoeObj.trigger = function trigger() {
const key = Array.prototype.shift.call(arguments); // 取出消息類型名稱
const fns = this.list[key]; // 取出該消息對(duì)應(yīng)的回調(diào)函數(shù)的集合
// 如果沒(méi)有訂閱過(guò)該消息的話,則返回
if(!fns || fns.length === 0) {
return;
}
for(var i = 0,fn; fn = fns[i++]; ) {
fn.apply(this,arguments); // arguments 是發(fā)布消息時(shí)附送的參數(shù)
}
};
// 小紅訂閱如下消息
shoeObj.listen('red',function(size){
console.log("尺碼是:"+size);
});
// 小花訂閱如下消息
shoeObj.listen('block',function(size){
console.log("再次打印尺碼是:"+size);
});
shoeObj.trigger("red",40);
shoeObj.trigger("block",42);
代碼封裝
我們知道,對(duì)于上面的代碼,小紅去買鞋這么一個(gè)對(duì)象shoeObj 進(jìn)行訂閱,但是如果以后我們需要對(duì)買房子或者其他的對(duì)象進(jìn)行訂閱呢,我們需要復(fù)制上面的代碼,再重新改下里面的對(duì)象代碼;為此我們需要進(jìn)行代碼封裝;
const event = {
list: {},
listen: function listen(key, fn) {
if (Array.isArray(this.list[key])) {
this.list[key].push(fn);
} else {
this.list[key] = [fn];
}
};
trigger: function trigger() {
const key = Array.prototype.shift.call(arguments); // 取出消息類型名稱
const fns = this.list[key]; // 取出該消息對(duì)應(yīng)的回調(diào)函數(shù)的集合
// 如果沒(méi)有訂閱過(guò)該消息的話,則返回
if(!fns || fns.length === 0) {
return;
}
for(var i = 0,fn; fn = fns[i++]; ) {
fn.apply(this,arguments); // arguments 是發(fā)布消息時(shí)附送的參數(shù)
}
};
};
我們?cè)诙x一個(gè)initEvent函數(shù),這個(gè)函數(shù)使所有的普通對(duì)象都具有發(fā)布訂閱功能,如下代碼:
var initEvent = function(obj) {
for(var i in event) {
obj[i] = event[i];
}
};
取消訂閱
event.cancel = function(key, fn){
// 如果存在該類型的消息
if(event.list[key] instanceof Array){
var i = event.list[type].length -1;
// 遍歷通知方法
for(; i >= 0 ; i--){
// 如果有通知該訂閱者的方法,則移除
// &&的短路機(jī)制
// splice的三種功能(刪除、替換、插入)之一
event.list[type][i] === fn && event.list[type].splice(i,1);
}
}
}
觀察者模式跟發(fā)布訂閱模式1
觀察者模式跟發(fā)布訂閱模式2
這篇文章我說(shuō)的 其實(shí)是觀察者模式。
在觀察者模式中,觀察者需要直接訂閱目標(biāo)事件;在目標(biāo)發(fā)出內(nèi)容改變的事件后,直接接收事件并作出響應(yīng)
在發(fā)布訂閱模式中,發(fā)布者和訂閱者之間多了一個(gè)發(fā)布通道;一方面從發(fā)布者接收事件,另一方面向訂閱者發(fā)布事件;訂閱者需要從事件通道訂閱事件