初始化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