觀察者模式又叫做釋出訂閱模式,它定義了一種一對多的關係,讓多個觀察者物件同時監聽某一個主題物件,這個主題物件的狀態發生改變時就會通知所有觀察著物件。 它是由兩類物件組成,主題和觀察者,主題負責釋出事件,同時觀察者通過訂閱這些事件來觀察該主體,釋出者和訂閱者是完全解耦的,彼此不知道對方的存在,兩者僅僅共享一個自定義事件的名稱。
在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》等,請多多關注。