EventEmitter簡介
EventEmitter是Node.js的內置模塊events提供的一個類,它是Node事件流的核心,EventEmitter是服務端的東西,
前端已經有event-emitter的npm庫
高級瀏覽器也有原生提供的EventTarget這種實現事件監聽和觸發的API
但是它們和Node.js的事件API都有或多或少的區別,今天我們就來實現一個前端版本的EventEmitter
我本章demo的github地址如下
API介紹
我們要實現的API有:
-
on(event, listener):為指定事件注冊一個監聽器,接受一個字符串 event 和一個回調函數。
-
emit(event, [arg1], [arg2]): 按監聽器的順序執行執行每個監聽器
-
addListener(event, listener):on的同名函數(alias)
-
once(event, listener): 和on類似,但只觸發一次,隨后便解除事件監聽
-
removeListener(event, listener): 移除指定事件的某個監聽回調
-
removeAllListeners([event]):移除指定事件的所有監聽回調
-
setMaxListeners(n):用於提高監聽器的默認限制的數量。(默認10監聽回調個產生警告)
-
listeners(event): 返回指定事件的監聽器數組。
為了保證兼容性和簡單性,下面的編碼全部基於ES5語法實現
構造函數
首先我們需要寫一個EventEmitter構造函數,給它設置兩個屬性listeners和maxListener
function EventEmitter() { this.listeners = {}; this.maxListener = 10; }
listeners用於存放事件監聽器函數,結構如下:
{ "event1": [f1,f2,f3], "event2": [f4,f5], ... }
而maxListener 是設置的某個事件能夠添加的監聽器的最大數量,超過這個值,需要在控制台輸出警告,但不會報錯阻止。按照Node的設計,這個值能夠通過setMaxListeners動態調整
on方法
-
判斷該事件的監聽器數量是否已超限,超限則報警告
-
判斷該事件監聽器數組是否初始化,若未初始化,則將listeners[event]初始化為數組,並加入監聽器cb
-
若監聽器數組已經被初始化,則判斷數組中是否已存在cb,不存在則添加,已存在則不做操作。
-
指定addListener等於on方法
EventEmitter.prototype.on = function (event, cb) { var listeners = this.listeners; if (listeners[event] && listeners[event].length >= this.maxListener) { throw console.error('監聽器的最大數量是%d,您已超出限制', this.maxListener) } if (listeners[event] instanceof Array) { if (listeners[event].indexOf(cb) === -1) { listeners[event].push(cb); } } else { listeners[event] = [].concat(cb); } } EventEmitter.prototype.addListener = EventEmitter.prototype.on;
emit方法
-
通過Array.prototype.slice.call(arguments)取出方法的參數列表args,(因為考慮簡單性和兼容性所以采用ES5的冗長編碼方式)
-
調用args.shift踢掉數組第一個參數即event,留下來的這些是要傳給監聽器的
-
遍歷監聽器,通過apply方法把上面得到的args參數傳進去
EventEmitter.prototype.emit = function (event) { var args = Array.prototype.slice.call(arguments); args.shift(); this.listeners[event].forEach(cb => { cb.apply(null, args); }); }
removeListener方法
-
通過indexOf確定監聽器回調在數組listeners[event]中的位置
-
通過splice(i,1)刪除之
EventEmitter.prototype.removeListener = function (event, listener) { var listeners = this.listeners; var arr = listeners[event] || []; var i = arr.indexOf(listener); if (i >= 0) { listeners[event].splice(i, 1); } }
once方法
once方法是on方法和removeListener方法的結合:用on方法監聽,在回調結束的最后位置,通過removeListener刪掉監聽函數自身
EventEmitter.prototype.once = function (event, listener) { var self = this; function fn() { var args = Array.prototype.slice.call(arguments); listener.apply(null, args); self.removeListener(event, fn); } this.on(event, fn) }
removeAllListener方法
清空listeners[event]數組
EventEmitter.prototype.removeAllListener = function (event) { this.listeners[event] = []; }
setMaxListeners方法和listeners方法
EventEmitter.prototype.listeners = function (event) { return this.listeners[event]; } EventEmitter.prototype.setMaxListeners = function (num) { this.maxListener = num; }