發佈訂閱模式(雛形)

初始化Event對象html

var initEvent = function(obj) {
    for(var i in event) {
        obj[i] = event[i];
    }
};

主代碼:設計模式

var event = {
    list: [],
    listen: function(key, fn) {
        // 肯定監聽的事件容器默認是一個空數組
        if(!this.list[key]) {
            this.list[key] = [];
        }
        // 訂閱的消息添加到緩存列表中
        this.list[key].push(fn);
        // 鏈式調用
        return this 
    },
    trigger: function(){
        // 獲取trigger 函數參數的第一個參數,即key鍵
        // 此時arguments 是trigger的參數類數組
        var key = Array.prototype.shift.call(arguments);
        // 拿到對應key的監聽事件數組
        var fns = this.list[key];
        // 若是沒有訂閱過該消息的話,則返回
        if(!fns || fns.length === 0) {
            return;
        }
        for(var i = 0, fn;i < fns.length; i ++) {
            fn = fns[i]
            //逐個調用key鍵所對應監聽事件數組函數
            // 此時arguments 一樣也是trigger的參數類數組,只不過少了第一個參數
            // 將此參數傳遞給fn函數做爲形參
            // this 也是fn的執行做用域
            fn.apply(this, arguments);
        }
    }
   };

調用執行:數組

// 新建一個好比小紅的對象
    var shoeObj = {};
    // 初始化小紅對象
    initEvent(shoeObj);
    // 小紅同時訂閱以下消息(鏈式調用) 
    shoeObj.listen('red',function(size, price){
        console.log("尺碼是:"+size);  
        console.log('price是' +price)
    }).listen('block', function (size, price) {
        console.log("尺碼是:"+size);  
        console.log('price是' +price)
    })
    
    shoeObj.trigger("red", 40, 500);

    shoeObj.trigger("block",42, 300);

訂閱了消息後,咱們可能會remove掉消息,因此Event對象新增一個方法:緩存

// 略
remove : function(key, fn) {
       var fns = this.list[key]
       // 若是key對應的消息沒有訂閱過的話,則返回
       if(!fns) return
       // 若是沒有傳入具體的回調函數,表示須要取消key對應消息的全部訂閱
       if(!fn) {
         fns.length = 0 // 或者this.list[key] = []
         // fns = []   
         //fns = [] 這樣寫後,實際this.list[key]中的回調數組
         //依然存在,由於初始fns指向this.list[key]這個數組(數組是一個引用類型)
         //fns = [],表明咱們將fns又指向了一個新的數組長度爲空的引用數組,而這個
         // 新的引用數組 和this.list[key]這個引用數組是計算機裏面佔用兩個不一樣的
         // 堆棧。
       }else {
         for(var i = 0; i < fns.length; i ++) {
           var _fn = fns[i]
           if(_fn === fn) {
              fns.splice(i, 1) // 刪除訂閱者的回調函數
           }
         }
       }
    },
// 略

調用app

// 小紅訂閱以下消息 同時在red上面訂閱了兩個消息
    // 注意fn1 和fn2 這種寫法,比較少見,實際fn1,fn2成爲了一個全局變量
    // 在remove的時候,做爲具體的參數傳遞
    shoeObj.listen('red',fn1 = function(size, price){
        console.log("尺碼是1----" +size);  
        console.log('price是1----' +price)
    }).listen('red', fn2 = function(size, price){
        console.log("尺碼是2----" +size);  
        console.log('price是2----' +price)
    })
    
    //remove 掉fn2
    shoeObj.remove('red', fn2)
    // 觸發回調 此時只會回調fn1
    shoeObj.trigger("red", 40, 500);
    
    //若是remove 不傳參數,就會將red中全部的監聽所有remove掉
    shoeObj.remove('red')
    shoeObj.trigger("red", 40, 500);

結束語
發佈訂閱模式是js中36中設計模式中最多見的模式,也是很重要的設計模式。其實咱們在寫項目邏輯代碼的時候,無形中也運用了這個思想,最多見的是click觸發回調。好比咱們定義一個方法,在定義的時候已經listen在一個對象上,或window對象上。在click觸發的時候,回調此方法,從而觸發函數。
發佈訂閱模式比較適合寫封裝插件,我認爲拿來寫業務邏輯代碼,有點不太好用。固然這只是我本身的觀點。
接下來,我準備用這個模式封裝一個上傳的組件。函數

附錄(參考文獻)this

  1. cn一篇博文
相關文章
相關標籤/搜索