【node不徹底指西】EventEmitter (事件發佈/訂閱模式)解析

從node異步編程解決方案提及吧:node

  • 事件發佈/訂閱模式
  • Promise/deferred模式
  • 流程控制庫

事件發佈/訂閱模式

事件監聽器模式是一種普遍運用於異步編程的模式,是回調函數的事件話,又稱發佈/訂閱模式。

主要實現的幾個功能包括編程

  • on
  • remove
  • once
  • emit

廢話少說,咱們來簡單的實現一個事件監聽函數吧數組

首先建立一個eventEmitter函數
function EventEmitter() {
    // 用Object.create(null)代替空對象{}
    // 好處是無雜質,不繼承原型鏈
    // _events來保存觀察着隊列的信息
    this._events = Object.create(null);
}

由於過多的偵聽器佔用大量內存,致使內存泄漏,因此偵聽器的個數通常不會超過10個,不然會有warnning警告⚠️
接下來是一些默認的設置異步

// 默認最多的綁定次數
EventEmitter.defaultMaxListeners = 10;
// 同on方法
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
// 返回監聽的事件名
EventEmitter.prototype.eventNames = function () {
    return Object.keys(this._events);
};
// 設置最大監聽數
EventEmitter.prototype.setMaxListeners = function (n) {
    this._count = n;
};
// 返回監聽數
EventEmitter.prototype.getMaxListeners = function () {
    return this._count ? this._count : this.defaultMaxListeners;
};
接下來是on函數的實現
EventEmitter.prototype.on = function (type, cb, flag) {
    // 不是newListener 就應該讓newListener執行如下
    if (type !== 'newListener') {
        this._events['newListener'] && this._events['newListener'].forEach(listener => {
            listener(type);
        });
    }
    if (this._events[type]) {
        // 根據傳入的flag來決定是向前仍是向後添加
        if (flag) {
            this._events[type].unshift(cb);
        } else {
            this._events[type].push(cb);
        }
    } else {
        this._events[type] = [cb];
    }
    // 監聽的事件不能超過了設置的最大監聽數
    if (this._events[type].length === this.getMaxListeners()) {
        console.warn('警告-監聽器Number過大');
    }
};

解析:
on函數是幫定的初始函數,首先判斷是不是首次進行偵聽,若是是的話,先進行一遍初始化函數
接下來在——events隊列裏找到指針爲type的地方,根據flag判斷是在隊列尾仍是頭加入callback函數異步編程

接下來是once監聽一次的實現方法
// 監聽一次
EventEmitter.prototype.once = function (type, cb, flag) {
    // 先綁定,調用後刪除
    function wrap() {
        cb(...arguments);
        this.removeListener(type, wrap);
    }
    // 自定義屬性
    wrap.listen = cb;
    this.on(type, wrap, flag);
};

解析:
實現爲在callback上包裝一層remove操做,再當作一個新的callback傳入on函數
這樣的的話在首次執行回調的時候就會執行remove操做,達到執行一次就刪除的操做函數

接下來是remove函數,刪除一個type的偵聽器
EventEmitter.prototype.removeListener = function (type, cb) {
    if (this._events[type]) {
        this._events[type] = this._events[type].filter(listener => {
            return cb !== listener && cb !== listener.listen;
        });
    }
};

解析:
傳入type和要刪除的callback,對type標記的數組進行 filter操做,假如cb cb === listener則過濾掉this

刪除全部
EventEmitter.prototype.removeAllListener = function () {
    this._events = Object.create(null);
};
接下來是發佈函數 emit
EventEmitter.prototype.emit = function (type, ...args) {
    if (this._events[type]) {
        this._events[type].forEach(listener => {
            listener.call(this, ...args);
        });
    }
};

解析:
也比較直觀,若是events裏面存在type的監聽器隊列,則隊列裏的每一個回調都執行一遍,而且用call函數綁定this和argprototype

完整代碼

//EventEmitter.js


function EventEmitter() {
    // 用Object.create(null)代替空對象{}
    // 好處是無雜質,不繼承原型鏈的東東
    this._events = Object.create(null);
}
// 默認最多的綁定次數
EventEmitter.defaultMaxListeners = 10;
// 同on方法
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
// 返回監聽的事件名
EventEmitter.prototype.eventNames = function () {
    return Object.keys(this._events);
};
// 設置最大監聽數
EventEmitter.prototype.setMaxListeners = function (n) {
    this._count = n;
};
// 返回監聽數
EventEmitter.prototype.getMaxListeners = function () {
    return this._count ? this._count : this.defaultMaxListeners;
};
// 監聽
EventEmitter.prototype.on = function (type, cb, flag) {
    // 默認值,若是沒有_events的話,就給它建立一個
    if (!this._events) {
        this._events = Object.create(null);
    }
    // 不是newListener 就應該讓newListener執行如下
    if (type !== 'newListener') {
        this._events['newListener'] && this._events['newListener'].forEach(listener => {
            listener(type);
        });
    }
    if (this._events[type]) {
        // 根據傳入的flag來決定是向前仍是向後添加
        if (flag) {
            this._events[type].unshift(cb);
        } else {
            this._events[type].push(cb);
        }
    } else {
        this._events[type] = [cb];
    }
    // 監聽的事件不能超過了設置的最大監聽數
    if (this._events[type].length === this.getMaxListeners()) {
        console.warn('警告-警告-警告');
    }
};
// 向前添加
EventEmitter.prototype.prependListener = function (type, cb) {
    this.on(type, cb, true);
};
EventEmitter.prototype.prependOnceListener = function (type, cb) {
    this.once(type, cb, true);
};
// 監聽一次
EventEmitter.prototype.once = function (type, cb, flag) {
    // 先綁定,調用後刪除
    function wrap() {
        cb(...arguments);
        this.removeListener(type, wrap);
    }
    // 自定義屬性
    wrap.listen = cb;
    this.on(type, wrap, flag);
};
// 刪除監聽類型
EventEmitter.prototype.removeListener = function (type, cb) {
    if (this._events[type]) {
        this._events[type] = this._events[type].filter(listener => {
            return cb !== listener && cb !== listener.listen;
        });
    }
};
EventEmitter.prototype.removeAllListener = function () {
    this._events = Object.create(null);
};
// 返回全部的監聽類型
EventEmitter.prototype.listeners = function (type) {
    return this._events[type];
};
// 發佈
EventEmitter.prototype.emit = function (type, ...args) {
    if (this._events[type]) {
        this._events[type].forEach(listener => {
            listener.call(this, ...args);
        });
    }
};

module.exports = EventEmitter;

個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/dev...指針

相關文章
相關標籤/搜索