【JavaScript】EventEmitter的前端實現

  • 2019 年 10 月 3 日
  • 筆記

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方法

  1. 判斷該事件的監聽器數量是否已超限,超限則報警告
  2. 判斷該事件監聽器數組是否初始化,若未初始化,則將listeners[event]初始化為數組,並加入監聽器cb
  3. 若監聽器數組已經被初始化,則判斷數組中是否已存在cb,不存在則添加,已存在則不做操作。
  4. 指定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方法

  1. 通過Array.prototype.slice.call(arguments)取出方法的參數列表args,(因為考慮簡單性和兼容性所以採用ES5的冗長編碼方式)
  2. 調用args.shift踢掉數組第一個參數即event,留下來的這些是要傳給監聽器的
  3. 遍歷監聽器,通過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方法

  1. 通過indexOf確定監聽器回調在數組listeners[event]中的位置
  2. 通過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;  }

 

Github地址

https://github.com/penghuwan/event-emitter