JavaScript原生實現觀察者模式

明月及時有發表於2017-12-14

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

在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的技術要點文章請看上海尚學堂:《JavaScript的文件物件模型DOM》;《js 大廈之JavaScript事件》;《細說JavaScript BOM》等,請多多關注。


相關文章