觀察者模式 觀察者模式也叫“訂閱者/發佈者”模式,定義對象間的一種一對多的依賴關係,發佈者可以向所有訂閱者發佈消息。 觀察者模式被廣泛地應用於JavaScript客戶端編程中。所有的瀏覽器事件(mouseover,keypress等)都是使用觀察者模式的例子。 使用這個模式的最主要目的就是促進對象之 ...
觀察者模式
觀察者模式也叫“訂閱者/發佈者”模式,定義對象間的一種一對多的依賴關係,發佈者可以向所有訂閱者發佈消息。
觀察者模式被廣泛地應用於JavaScript客戶端編程中。所有的瀏覽器事件(mouseover,keypress等)都是使用觀察者模式的例子。
使用這個模式的最主要目的就是促進對象之間的解耦(弱化對象之間的聯繫)。在觀察者模式中,一組對象訂閱另一個對象的指定活動並得到通知。
如:
document.body.addEventListener('click',function(){
alert(2);
},false);
以上代碼訂閱了document.body的click事件,當body被點擊時,body便會向訂閱者發佈消息;
實現觀察者模式的一般步驟:
- 先指定誰充當發佈者;
- 給發佈者添加一個緩存列表,用於存放回調函數以便通知訂閱者;
- 發佈消息時,發佈者會遍歷這個緩存列表,依次觸發裡面存放的訂閱者回調函數;還可在回調函數內加入一些參數;
(也可先發佈消息,將消息保存起來,當有訂閱者時就將該消息重新發佈給他)
發佈-訂閱模式的通用實現:
var Publisher = (function(){
//listenerList用來保存一系列的key,每個key為保存了所有訂閱者函數fn的數組{ keyA:[fn1,fn2...], keyB:[...]...}
//類似:可以為同一事件註冊多個回調函數
var listenerList = {};
return {
addListener: function(key,fn){
if(!listenerList[key]){
listenerList[key] = [];
}
if(listenerList[key].indexOf(fn) === -1){ //檢測fn是否已訂閱,註意indexOf方法不支持IE9以下,可用for迴圈代替
listenerList[key].push(fn);
}
},
publish: function(){
var key = Array.prototype.shift.call(arguments),
fns = listenerList[key];
if(!fns||fns.length === 0){
return false;
}
for(var i = 0,fn;fn = fns[i++];){
fn.apply(this,arguments);
}
},
removeListener: function(key,fn){
var fns = listenerList[key];
if(!fns){
return false;
}
if(!fn){ //若沒指定fn,則表示取消所有訂閱
fns && (fns.length = 0);
}else{ //可直接用fns.splice(listenerList[key].indexOf(fn),1);但indexOf方法不支持IE9以下
for(var l = fns.length - 1; l >= 0; l--){
if(fns[l] === fn){
fns.splice(l,1);
}
}
}
}
};
})();
Publisher.addListener('month',function(num){ //訂閱month
console.log('本月數量: '+num);
});
Publisher.addListener('month',function(price){
console.log('本月價格: '+price);
});
Publisher.addListener('week',function(num){
console.log('本周數量: '+num);
});
Publisher.publish('month',123); //手動發佈消息
//本月數量: 123
//本月價格: 123
Publisher.publish('week',5);
//本周數量: 5
實際應用:
$.ajax('http://xxx.com?login',function(data){
Publisher.publish('loginSuccess',data); //若登錄成功則向所有訂閱者發佈登錄成功的消息
});
//以下在各個模塊添加訂閱消息
var header = (function () {
Publisher.addListener('loginSuccess', function (data) {
header.setAvatar(data.avatar);
});
return {
setAvatar: function (data) {
console.log('設置header模塊的頭像')
}
}
})();
var nav = (function () {
Publisher.addListener('loginSuccess', function (data) {
nav.setAvatar(data.avatar);
});
return {
setAvatar: function (avatar) {
console.log('設置header模塊的頭像')
}
}
})();
//...可隨意添加其他模塊
參考文獻:
《JavaScript模式》
《JavaScript設計模式與開發實踐》