又叫做觀察者模式,定義對(duì)象之中一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí)候溢谤,所有依賴于它的對(duì)象都將得到通知。
一憨攒、應(yīng)用
- 廣泛應(yīng)用于異步編程之中世杀,是一種替代回調(diào)函數(shù)的方案。訂閱一個(gè)事件肝集,發(fā)生操作A(對(duì)操作A監(jiān)聽(tīng))之后瞻坝,事件發(fā)生.
- 取代對(duì)象之間硬編碼的通知機(jī)制,一個(gè)對(duì)象不用再顯示的調(diào)用另外一個(gè)對(duì)象的某個(gè)接口杏瞻,讓兩個(gè)對(duì)象松耦合的聯(lián)系在一起所刀,雖然不清楚彼此的細(xì)節(jié)衙荐,但是不影響他們之間相互通信。
1. Dom事件
一種典型的發(fā)布訂閱者模式浮创,一個(gè)事件(‘click忧吟,mousedown等’)對(duì)一個(gè)dom節(jié)點(diǎn)進(jìn)行監(jiān)聽(tīng),操作dom節(jié)點(diǎn)斩披,相應(yīng)的觸發(fā)事件溜族,響應(yīng)函數(shù)執(zhí)行。事件函數(shù)對(duì)dom節(jié)點(diǎn)完全未知垦沉,不用去理會(huì)事件函數(shù)內(nèi)容煌抒,發(fā)布就好。
2. 自定義事件
- 指定發(fā)布者
- 給發(fā)布者一個(gè)緩存列表厕倍,一個(gè)對(duì)象由n個(gè)鍵值對(duì)組成,鍵表示事件名,值是一個(gè)由事件處理程序組成的數(shù)組摧玫,相當(dāng)于訂閱者的花名冊(cè)
- 發(fā)布消息,遍歷緩存列表绑青,依次執(zhí)行訂閱者的回調(diào)函數(shù)诬像。
自定義事件SF
3. 全局的發(fā)布訂閱模式
問(wèn)題:
- 每一個(gè)發(fā)布者對(duì)象都有l(wèi)isten和trigger方法,以及一個(gè)緩存列表clientList闸婴,浪費(fèi)資源
- 發(fā)布者和訂閱者還有一定的耦合關(guān)系:
發(fā)布者.listen(“事件名”坏挠,function(){})
所以【對(duì)于全局模式下的訂閱者】:
- 使用一個(gè)全局的Event對(duì)象來(lái)實(shí)現(xiàn),訂閱者和發(fā)布者不知道相互之間是誰(shuí)邪乍,相當(dāng)于一個(gè)傳遞消息的“中介”:
Event.listen("事件名"降狠,function(){})
4. 模塊之間通信
- 全局模式下的訂閱者而言,兩個(gè)封裝良好的模塊之間的通信是通過(guò)Event對(duì)象來(lái)進(jìn)行的庇楞,比如兩個(gè)模塊都對(duì)Event.count來(lái)進(jìn)行處理榜配,從而實(shí)現(xiàn)模塊之間通信。
- 這樣的情況之下吕晌,模塊之間的聯(lián)系被隱藏蛋褥,所以需要首先定義好為其他模塊之間通信服務(wù)的暴漏性模塊,他的作用就是暴露出一部分接口睛驳,供其他模塊使用烙心。
5. 全局事件的命名沖突
//全局作用域下的發(fā)布訂閱模式
(function(){
var Event = (function{
var global = this,
Modal,
_default = 'default';
Event = function(){
var _listen,
_trigger,
_remove,
_slice = Array.prototype.slice,
_shift = Array.prototype.shift,
_unshift = Array.prototype.unshift,
namespaceCache = {},
_create,
find,
each = function(ary,fn){
var ret ;
for(const i = 0,l = ary.length; i < l;i ++){
var n = ary[i];
ret = fn.call(n,i,n);
}
return ret;
};
_listen = function(key,fn,cache){
if(!cache[key]){
cache[key] = [];
}
cache[key].push(fn);
};
_remove = function(key,cache,fn){
if(cache[key]){
if(fn){
for(var i = cache[key].length;i>=0;i--){
if(cache[key] === fn){
cache[key].splice(i,1);
}
}
}else{
cache[key] = [];
}
}
};
_trigger = function(){
var cache = _shift.call(arguments),
key = _shift.call(arguments),
args = arguments,
_self = this,
ret,
stack = cache[key];
if(!stack || !stack.length){
return;
}
return each(stack,function(){
return this.apply(_self,args);
});
};
_create = function(namespace){
var namespace = namespace || _default;
var cache = {},
offlineStack = [],
ret = {
listen:function(key,fn,last){
_listen(key,fn,cache);
if(offlineStack === null){
return;
}
if(last === 'last'){
offlineStack.length && offlineStack.pop()();
}else{
each(offlineStack,function(){
this();
});
}
offlineStack = null;
},
one:function(key,fn,last){
_remove(key,cache);
this.listen(key,cache,fn);
},
remove:function(key,fn){
_remove(key,cache,fn);
},
trigger:function(){
var fn,
args,
_self = this;
_unshift.call(arguments,cache);
args = arguments;
fn = function(){
return _trigger.apply(_self,args);
};
if(offlineStack){
return offlineStack.push(fn);
}
return fn;
}
};
return namespace ?
(namespaceCache[namespace] ? namespaceCache[namespace] : namespaceCache[namespace] = ret)
: ret;
};
return {
create : ,
one: ,
remove: ,
listen:,
trigger:,
var event = this.create();
event.trigger.apply(this,arguments);
}
}();
return Event;
}());
二、遇到的問(wèn)題:
- 問(wèn)題: 一個(gè)modal組件開(kāi)發(fā)的過(guò)程中乏沸,業(yè)務(wù)邏輯編寫(xiě)完之后淫茵,開(kāi)始進(jìn)行組件化的整合,需要預(yù)留出一個(gè)可被實(shí)例化出一個(gè)組建的構(gòu)造函數(shù)蹬跃,一些開(kāi)放字段匙瘪,一些構(gòu)造函數(shù)的原型方法。要求代碼符合組件化規(guī)范,可復(fù)用丹喻,易擴(kuò)展薄货,松耦合
- 解決方案: 發(fā)布訂閱模式,一個(gè)組件Modal當(dāng)做發(fā)布者驻啤,里面所有的方法菲驴,預(yù)留參數(shù)當(dāng)做訂閱者,耦合性較松骑冗,封裝又比較嚴(yán)密赊瞬。
....未完待續(xù)