發佈訂閱模式還不會??戳這裏,50行核心代碼,手把手教你學會

小插曲

事件

  • 建議你們看下官網中events事件的描述node中events事件
  • 發佈訂閱模式定義了一種一對多的依賴關係
  • 在Node中EventEmitter開放on(事件名,回調函數)用於訂閱事件
  • emit(事件名)用於發佈事件,可能對應多個訂閱事件,讓訂閱事件依次執行

不明白?不要緊。舉個最簡單例子,女人失戀了會哭,還會找新男友,在這裏哭和找男友至關於訂閱女人失戀的回調,何時執行呢?當發佈女人失戀這件事的時候,說的這麼抽象,直接來一段代碼吧html

  • 是否是很簡單,只有發佈這個事件時候,被訂閱的事件纔會依次執行,造成一對多的依賴關係。接下來直接寫源碼實現

思路構建

  • 咱們想構造一個相似這樣的對象 {"失戀":[cry,findBoy]},當事件發佈的時候,讓數組中對應的函數依次執行,就實現了這樣的效果
  • 先講個小知識點 {}和Object.create(null)區別。 {}有做用鏈,經過Object.create(null)創造的空對象沒有做用鏈,給你們演示下,其它就沒啥區別。源碼就是這樣寫(逼格高)

實現events模塊

一、on和emit 兩個核心方法

  • 源碼實現
// 聲明EventEmitter事件發生器構造函數
function EventEmitter() {
    this._events = Object.create(null);
}
//on 訂閱方法實現  由於在實例上調用,因此寫在原型上
EventEmitter.prototype.on = function(type,callback){
    // 若是實例不存在則建立一個空對象,Object.create(null)沒有鏈
    if(!this._events) {
        this._events = Object.create(null);
    }
    if(this._events[type]){ //若是失戀有對應的值,直接往數組push
        this._events[type].push(callback)
    }else { //第一次訂閱,沒有失戀,就聲明{失戀:[cry]}
        this._events[type] = [callback];
    }
};
// emit方法實現
EventEmitter.prototype.emit = function(type){
    if(this._events[type]){ //{失戀:[cry,eat]} 若是失戀對應有值,依次執行裏面的方法
        this._events[type].forEach(fn=>fn())
    }
};
module.exports = EventEmitter
複製代碼
  • 十幾行代碼就實現核心功能,這麼簡單?對 就是這麼簡單,趕快來測試下吧

二、removeListener 取消訂閱事件,失戀了不想哭了,因此咱們提供個移除監聽的方法

  • 比較簡單,直接上代碼吧看的直接
// 移除訂閱事件的方法
EventEmitter.prototype.removeListener = function(type,callback){
    if(this._events[type]){
        // 返回false就表示不要了,用filter實現去重
        this._events[type] = this._events[type].filter(fn=>fn!==callback)
    }
};
複製代碼
  • 測試下吧,失戀了不想哭了
  • 完美實現,是否是很激動。

三、removeAllListeners移除所有的監聽器,與removeListener相對應

// removeAllListeners 移除全部的監聽者
EventEmitter.prototype.removeAllListeners = function(){
//簡單粗暴,直接賦值空對象 {}
    this._events = Object.create(null);
};
複製代碼
  • 測試下,失戀了既不想哭,也不想找對象,什麼也不打印就對拉

四、擴展once方法 咱們但願哭的事件 屢次發佈emit時候只執行一次,也就表明執行一次後須要將事件從對應關係中移除掉。

// once實現
EventEmitter.prototype.once = function(type,callback,flag){
    // 先綁定 調用後再刪除,運用了one函數 {失戀:one}
    let one = (...args)=> {
        callback(...args);
        this.removeListener(type, one);
    }
    //自定義屬性 由於實例中沒有one屬性
    one.l = callback;
    this.on(type,one)
};
// 移除訂閱事件的方法
EventEmitter.prototype.removeListener = function(type,callback){
    if(this._events[type]){
        // 返回false就表示不要了,用filter實現去重
        this._events[type] = this._events[type].filter(fn=>fn!==callback && fn.l!==callback)
    }
};
複製代碼
  • 你可能會疑惑爲何聲明一個wrap函數,設想下,否則你告訴我怎麼先綁定一次,在移除。不少人可能都會這麼寫
  • 錯誤例子 錯誤例子 錯誤例子(重要事情說三遍)
// - 錯誤例子 錯誤例子 錯誤例子(重要事情說三遍)
//你可能會這麼寫,但剛綁定就移除拉,體會這意思了吧
EventEmitter.prototype.once = function(type,callback){
//先綁定在移除
    this.on(type,callback);
    this.removeListener(type,callback)
};
複製代碼
  • 測試下吧,一步一個腳印

五、newListener方法。當cry添加到內部監聽數組({失戀:[cry]})以前,會觸發自身的'newListener'事件

  • 沒聽懂?咱們先來看官方的用法

簡單說就是能夠監控到訂閱的事件類型,上源碼看下如何實現node

//on 訂閱方法實現  由於在實例上調用,因此寫在原型上
EventEmitter.prototype.on = function(type,callback){
    // 若是實例不存在則建立一個空對象,Object.create(null)沒有鏈
    if(!this._events) {
        this._events = Object.create(null);
    }
    if(type!=="newListener"){
        if(this._events["newListener"]){
            this._events["newListener"].forEach(fn=>fn(type))
        }
    }
    if(this._events[type]){ //若是失戀有對應的值,直接往數組push
        this._events[type].push(callback)
    }else { //第一次訂閱,沒有失戀,就聲明{失戀:[cry]}
        this._events[type] = [callback];
    }
};
複製代碼
  • 測試下吧

看到這裏,基本方法都實現了。不經常使用就不解釋拉。 若是你們想看全部源碼方法解析,能夠點進我github上參考

相關文章
相關標籤/搜索