發佈-訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知。JavaScript開發中咱們通常用事件模型來代替傳統的發佈-訂閱模式前端
小明最近喜歡上吃老北京燒餅,但是到了賣燒餅的地方發現已經賣完了,並且排隊的人還不少.幸運的是賣燒餅那個MM看小明長得帥,告訴小明等一會就有燒餅吃啦!但是小明如今還有約會要去,不知道燒餅能何時出鍋,總不能由於吃燒餅而不去約會吧!這時候小明靈機一動,說燒餅MM把你電話給我吧!我先去忙,等會打電話問你燒餅好了沒有。燒餅MM也沒想太多,把電話給小明瞭。後來小龍也來買燒餅,狀況跟小明差很少,小龍也把燒餅MM的電話要走了。但是問題就這來了,小明、小龍一下子打一個電話給燒餅MM,致使燒餅MM很煩,辭職走了不幹了。
經過上邊的事情咱們能夠發現,存在好多問題
第一:賣燒餅的MM應該充當發佈者
第二:小明小龍的電話應該保存在賣燒餅的用戶列表中,若是賣燒餅的MM離職,這用戶就會丟失
第三:實際上沒有這麼笨蛋的銷售方式的編程
賣燒餅的店主能夠把小明、小龍的電話記錄下來,等店裏有燒餅了在通知小龍小明來拿這就是所謂的發佈-訂閱模式,代碼以下:設計模式
/*燒餅店*/
var Sesamecakeshop={
clienlist:[],//緩存列表
addlisten:function(fn){//增長訂閱者
this.clienlist.push(fn);
},
trigger:function(){//發佈消息
for(var i=0,fn;fn=this.clienlist[i++];){
fn.apply(this,arguments);
}
}
}
/*小明發布訂閱*/
Sesamecakeshop.addlisten(function(price,taste){
console.log("小明發布的"+price+"元,"+taste+"味道的");
});
/*小龍發佈訂閱*/
Sesamecakeshop.addlisten(function(price,taste){
console.log("小龍發佈的"+price+"元,"+taste+"味道的");
});
Sesamecakeshop.trigger(10,"椒鹽");複製代碼
從代碼中能夠看出,只有小明,小龍預約了燒餅,燒餅店就能夠發佈消息告訴小龍與小明。可是有個問題不知道你們發現了沒有。小明只喜歡椒鹽味道的。而小龍只喜歡焦糖味道的。上面的代碼就知足不了客戶的需求,給客戶一種感受就是,無論我喜歡不喜歡,你都會發給我。若是發佈比較多,客戶就會感到厭煩,甚至會想刪除訂閱。下邊是對代碼進行改良你們能夠看看。緩存
/*燒餅店*/
var Sesamecakeshop={
clienlist:{},/*緩存列表*/
/**
* 增長訂閱者
* @key {String} 類型
* @fn {Function} 回掉函數
* */
addlisten:function(key,fn){
if(!this.clienlist[key]){
this.clienlist[key]=[];
}
this.clienlist[key].push(fn);
},
/**
* 發佈消息
* */
trigger:function(){
var key=[].shift.call(arguments),//取出消息類型
fns=this.clienlist[key];//取出該類型的對應的消息集合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
},
/**
* 刪除訂閱
* @key {String} 類型
* @fn {Function} 回掉函數
* */
remove:function(key,fn){
var fns=this.clienlist[key];//取出該類型的對應的消息集合
if(!fns){//若是對應的key沒有訂閱直接返回
return false;
}
if(!fn){//若是沒有傳入具體的回掉,則表示須要取消全部訂閱
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍歷回掉函數列表
if(fn===fns[l]){
fns.splice(l,1);//刪除訂閱者的回掉
}
}
}
}
}
/*小明發布訂閱*/
Sesamecakeshop.addlisten("焦糖",fn1=function(price,taste){
console.log("小明發布的"+price+"元,"+taste+"味道的");
});
/*小龍發佈訂閱*/
Sesamecakeshop.addlisten("椒鹽",function(price,taste){
console.log("小龍發佈的"+price+"元,"+taste+"味道的");
});
Sesamecakeshop.trigger("椒鹽",10,"椒鹽");
Sesamecakeshop.remove("焦糖",fn1);//注意這裏是按照地址引用的。若是傳入匿名函數則刪除不了
Sesamecakeshop.trigger("焦糖",40,"焦糖");複製代碼
刪除的時候須要注意的是,若是訂閱的時候傳遞的是匿名函數,刪除的時候若是傳入的也是匿名函數。則刪除不了。由於刪除時候是按照地址引用刪除的。傳進去的兩個匿名函數,對應的地址引用是不一樣的。bash
好比我們常見的用戶身份分別有不一樣的功能,超級管理員擁有最高權限,能夠刪除修改任意用戶。而普通用戶則只能修改本身的帳戶信息。首先是用戶身份驗證,驗證經過以後對應功能才能夠顯示。前端工程師
//登陸發佈-訂閱模式
login={
clienlist:{},/*緩存列表*/
/**
* 增長訂閱者
* @key {String} 類型
* @fn {Function} 回掉函數
* */
addlisten:function(key,fn){
if(!this.clienlist[key]){
this.clienlist[key]=[];
}
this.clienlist[key].push(fn);
},
/**
* 發佈消息
* */
trigger:function(){
var key=[].shift.call(arguments),//取出消息類型
fns=this.clienlist[key];//取出該類型的對應的消息集合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
}
}
//超級管理員修改全部用戶
var editall=(function(){
login.addlisten("loginsucc",function(data){
editall.setview(data);
});
return{
setview:function(data){
console.log(data);
console.log("超級管理員修改全部用戶");
}
}
})();
//僅僅修改本身
var editOwn=(function(){
login.addlisten("loginsucc",function(data){
editOwn.setview(data);
});
return{
setview:function(data){
console.log(data);
console.log("僅僅修改本身");
}
}
})();複製代碼
發佈-訂閱模式簡單封裝架構
var _Event=(function(){
var clienlist={},
addlisten,trigger,remove;
/**
* 增長訂閱者
* @key {String} 類型
* @fn {Function} 回掉函數
* */
addlisten=function(key,fn){
if(!clienlist[key]){
clienlist[key]=[];
}
clienlist[key].push(fn);
};
/**
* 發佈消息
* */
trigger=function(){
var key=[].shift.call(arguments),//取出消息類型
fns=clienlist[key];//取出該類型的對應的消息集合
if(!fns || fns.length===0){
return false;
}
for(var i=0,fn;fn=fns[i++];){
fn.apply(this,arguments);
}
};
/**
* 刪除訂閱
* @key {String} 類型
* @fn {Function} 回掉函數
* */
remove=function(key,fn){
var fns=clienlist[key];//取出該類型的對應的消息集合
if(!fns){//若是對應的key沒有訂閱直接返回
return false;
}
if(!fn){//若是沒有傳入具體的回掉,則表示須要取消全部訂閱
fns && (fns.length=0);
}else{
for(var l=fns.length-1;l>=0;l--){//遍歷回掉函數列表
if(fn===fns[l]){
fns.splice(l,1);//刪除訂閱者的回掉
}
}
}
};
return{
addlisten:addlisten,
trigger:trigger,
remove:remove
}
})();
_Event.addlisten("jianbing",function(d,all){
console.log("發佈的消息來自:"+d+",具體信息:"+all);
});
_Event.addlisten("jianbing",function(d,all){
console.log("發佈的消息來自:"+d+",具體信息:"+all);
})
_Event.trigger("jianbing","小小坤","前端工程師,擅長JavaScript,喜歡結交更多的前端技術人員,歡迎喜歡技術的你加QQ羣:198303871")複製代碼
發佈-訂閱模式就是常說的觀察者模式,在實際開發中很是有用。它的優勢是爲時間是解耦,爲對象之間解構,它的應用很是普遍,既能夠在異步編程中也能夠幫助咱們完成更鬆的解耦。發佈-訂閱模式還能夠幫助咱們實現設計模式,從架構上來看,不管MVC仍是MVVC都少不了發佈-訂閱模式的參與。然而發佈-訂閱模式也存在一些缺點,建立訂閱自己會消耗必定的時間與內存,也許當你訂閱一個消息以後,以後可能就不會發生。發佈-訂閱模式雖然它弱化了對象與對象之間的關係,可是若是過分使用,對象與對象的必要聯繫就會被深埋,會致使程序難以跟蹤與維護。app