背景
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模型實現完成,歡迎點評交流。