觀察者模式又叫做發佈訂閱模式,它定義了一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態發生改變時就會通知所有觀察著對象。 它是由兩類對象組成,主題和觀察者,主題負責發佈事件,同時觀察者通過訂閱這些事件來觀察該主體,發佈者和訂閱者是完全解耦的,彼此不知道對方的存在,兩者僅僅 ...
觀察者模式又叫做發佈訂閱模式,它定義了一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態發生改變時就會通知所有觀察著對象。 它是由兩類對象組成,主題和觀察者,主題負責發佈事件,同時觀察者通過訂閱這些事件來觀察該主體,發佈者和訂閱者是完全解耦的,彼此不知道對方的存在,兩者僅僅共用一個自定義事件的名稱。
在Nodejs中通過EventEmitter實現了原生的對於這一模式的支持。
在JavaScript中事件監聽機制就可以理解為一種觀察者模式。通過onclick進行事件綁定,然後通過交互行為進行觸發或者事件主動觸發。
下麵給出一個JS自定義的PubSub,仔細閱讀下麵這段代碼有助於你理解觀察者模式。
一、定義觀察者類Pubsub
/* Pubsub */
function Pubsub(){
//存放事件和對應的處理方法
this.handles = {};
}
二、實現事件訂閱on
//傳入事件類型type和事件處理handle
on: function (type, handle) {
if(!this.handles[type]){
this.handles[type] = [];
}
this.handles[type].push(handle);
}
三、實現事件發佈emit
emit: function () {
//通過傳入參數獲取事件類型
var type = Array.prototype.shift.call(arguments);
if(!this.handles[type]){
return false;
}
for (var i = 0; i < this.handles[type].length; i++) {
var handle = this.handles[type][i];
//執行事件
handle.apply(this, arguments);
}
}
需要說明的是Array.prototype.shift.call(arguments)這句代碼,arguments對象是function的內置對象,可以獲取到調用該方法時傳入的實參數組。
shift方法取出數組中的第一個參數,就是type類型。
四、實現事件取消訂閱off
off: function (type, handle) {
handles = this.handles[type];
if(handles){
if(!handle){
handles.length = 0;//清空數組
}else{
for (var i = 0; i < handles.length; i++) {
var _handle = handles[i];
if(_handle === handle){
handles.splice(i,1);
}
}
}
}
}
完整代碼:
/* Pubsub */
function Pubsub(){
//存放事件和對應的處理方法
this.handles = {};
}
Pubsub.prototype={
//傳入事件類型type和事件處理handle
on: function (type, handle) {
if(!this.handles[type]){
this.handles[type] = [];
}
this.handles[type].push(handle);
},
emit: function () {
//通過傳入參數獲取事件類型
var type = Array.prototype.shift.call(arguments);
if(!this.handles[type]){
return false;
}
for (var i = 0; i < this.handles[type].length; i++) {
var handle = this.handles[type][i];
//執行事件
handle.apply(this, arguments);
}
},
off: function (type, handle) {
handles = this.handles[type];
if(handles){
if(!handle){
handles.length = 0;//清空數組
}else{
for (var i = 0; i < handles.length; i++) {
var _handle = handles[i];
if(_handle === handle){
handles.splice(i,1);
}
}
}
}
}
}
五、測試
var p1 = new Pubsub();
p1.on('mm', function (name) {
console.log('mm: '+ name);
});
p1.emit('mm','哈哈哈哈');
console.log('===============');
var p2 = new Pubsub();
var fn = function (name) {
console.log('mm2: '+ name);
};
var fn2 = function (name) {
console.log('mm222: '+ name);
};
p2.on('mm2', fn);
p2.on('mm2', fn2);
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
p2.off('mm2', fn);
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
p2.off('mm2');
p2.emit('mm2','哈2哈2哈2哈2');
console.log('-------------');
有關JavaScript的技術要點文章請看上海尚學堂:《JavaScript的文檔對象模型DOM》;《js 大廈之JavaScript事件》;《細說JavaScript BOM》等,請多多關註。