Node中EventEmitter理解與簡單實現

Meteor發表於2018-01-26

背景

node的招牌技能是“事件驅動,非阻塞IO”,可以看到事件在node中的重要性。
在瀏覽器端存在各種事件繫結(click、mousedown...),node中也存在各種事件繫結(fs.read、http.creaeServer...)。
在node中還有一個自定義事件EventEmitter,官網寫到很多node的api都是基於EventEmitter。

使用

一般我們會通過繼承來使用EventsEmitter,單獨new一個EventEmitter似乎除了驗證方法沒有意義。

//引入events模組
const EventEmitter = require('events');
//定義一個MyEmitter類繼承EventEmiteer
class MyEmitter extends EventEmitter {}

//new一個MyEmitter物件
const myEmitter = new MyEmitter();
//給myEmitter物件繫結一個event事件
myEmitter.on('event', () => {
  console.log('an event occurred!');
});
//觸發event事件
myEmitter.emit('event');    //輸出 "an event occurred!"
複製程式碼

這裡我們實現對EventEmitter繼承,繫結事件監聽函式,觸發事件。

EventEmitter簡單實現

簡單原理:在EventEmitter下建立一個events物件。以on監聽的事件名為鍵名,鍵值是監聽的函式的組合為一個陣列。

1.建立EventEmitter物件

function EventEmitter() {
    this.events = {};  //所有事件監聽函式放在這個物件裡儲存
    this._maxListeners = 10; //監聽函式最多10個
}
複製程式碼

2.給EventEmitter增加on、addEventListener繫結事件

//給制定函式繫結事件處理函式
EventEmitter.prototype.on = EventEmitter.prototype.addEventListener = function (type,listener) {
    if(this.events[type]){
        this.events[type].push(listener);
        if(this._maxListeners != 0 && this.events[type].length > this._maxListeners){
            console.error('MaxListenersExceededWarning: Possible EventEmitter memory leak detected.\n');
        }
    }else {
        this.events[type] = [listener];
    }
}

複製程式碼

3.觸發事件

EventEmitter.prototype.emit = function (type, ...rest) {
    if(this.events[type]){
        //遍歷觸發函式陣列   apply把this指向當前物件  解構rest
        this.events[type].forEach((listener)=>listener.apply(this,rest));
    }
}
複製程式碼

4.移除監聽函式

EventEmitter.prototype.removeListener =  function(type,listener) {
   if (this.events[type]){
       this.events[type] = this.events[type].filter(l=>l!=listener);
       //使用filter過濾陣列
   }
}
複製程式碼

簡單的EventEmitter模型實現完成,歡迎點評交流。

相關文章