摘要:實例方法的話,最核心的就是分別是添加事件,刪除事件,發布事件。為了防止進程崩潰,可以在對象的事件上注冊監聽器,或使用模塊。注意,模塊已被廢棄。作為最佳實踐,應該始終為事件注冊監聽器。
前言
事件在js中非常的常見,不管是瀏覽器還是node,這種事件發布/訂閱模式的應用都是很常見的。至于發布/訂閱模式和觀察者模式是否是同一種設計模式說法都有,這里不做具體的討論。在之前的項目中也曾自己實現過一個事件模塊,核心還是一個EventEmitter。下文就要結合node中的event模塊分析一下,一個EventEmitter應該如何實現,有什么注意點。
源碼地址
https://github.com/nodejs/nod...
首先第一步就是一個EventEmitter的類,然后考慮一下這個類的實例屬性和實例方法。
實例屬性的話最基礎的就是一個eventMap,可以是一個空對象,當然也可以這樣創建Object.create(null)。如果需要還可以增加maxListener之類的屬性。
實例方法的話,最核心的就是add delete emit分別是添加事件,刪除事件,發布事件。當然實際實現的時候,例如count,has,once(一次性添加),preAdd(添加在事件隊列最前面),這些方法則是可以根據實際需求去添加。
以下代碼均為簡化的偽代碼
add方法EventEmitter.prototype.add = function(type, fn) { if (!isFunction(fn)) return;//判斷是否在監聽中添加的是合法的函數 //判斷type是否添加過,添加過一個還是多個函數 if (this.event[type]) { if (isArray(this.event[type])){ //如果想要實現preadd將push改為unshift即可 this.event[type].push(fn); } else { //如果想要實現preadd改變順序 this.event[type] = [this.event[type], fn]; } } else { this.event[type] = fn; } }once方法
參考一下node的once方法
function onceWrapper(...args) { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; Reflect.apply(this.listener, this.target, args); } } function _onceWrap(target, type, listener) { var state = { fired: false, wrapFn: undefined, target, type, listener }; var wrapped = onceWrapper.bind(state); wrapped.listener = listener; state.wrapFn = wrapped; return wrapped; } EventEmitter.prototype.once = function once(type, listener) { this.on(type, _onceWrap(this, type, listener)); return this; };
函數用onceWrap包裹,運行前需要對添加的監聽進行移除
delete很簡單理清楚幾種邊界情況就可以了
EventEmitter.prototype.delete = function(type, fn) { //直接刪除整類監聽 if(fn === undefined){ this.events[type] && delete this.events[type]; }else{ //判斷fn合法性就省了 if(this.events[type]) { if (this.events[type] === fn) { delete this.events[type]; } else { for (var i in this.events[type]) { if(this.events[type][i] === fn){ if (i === 0) { this.events[type].shift(); } else { this.events[type].splice(i,1); } } } if(this.events[type].length === 1) this.events[type] = this.events[type][0]; } } } }emit
EventEmitter.prototype.emit = function(type) { //獲取參數 var args = [].slice.call(arguments, 1); var handler = events[type]; if (handler === undefined) return false; if (typeof handler === "function") { handle.apply(this, args); } else { var len = handler.length; const listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) handle[i].apply(this, args); } }
發布事件有兩個注意點,一個是注意參數的保留,另一個則是上下文,這里上下文是直接取了event實例的上下文,也可以考慮一下手動傳入上下文的形式,或者說fn在定義的時候直接寫成箭頭函數,也可以避免上下文成為eventEmit實例的上下文。
錯誤處理這里提一下node的event的錯誤事件
當 EventEmitter 實例中發生錯誤時,會觸發一個 "error" 事件。 這在 Node.js 中是特殊情況。
如果 EventEmitter 沒有為 "error" 事件注冊至少一個監聽器,則當 "error" 事件觸發時,會拋出錯誤、打印堆棧跟蹤、且退出 Node.js 進程。
為了防止 Node.js 進程崩潰,可以在 process 對象的 uncaughtException 事件上注冊監聽器,或使用 domain 模塊。 (注意,domain 模塊已被廢棄。)
作為最佳實踐,應該始終為 "error" 事件注冊監聽器。
如果有需要在自己的實踐中也可以增加一個錯誤處理的機制,保證event實例的穩定性
總結一個事件訂閱發布類其實不難實現,而在node中有很多厲害的類都是繼承的事件類,而之后我會接著對node文件系統進行學習
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93996.html
摘要:本文從的的使用出發,循序漸進的實現一個完整的模塊。移除指定事件的某個監聽器,監聽器必須是該事件已經注冊過的監聽器的別名移除所有事件的所有監聽器,如果指定事件,則移除指定事件的所有監聽器。返回指定事件的監聽器數組。 node的事件模塊只包含了一個類:EventEmitter。這個類在node的內置模塊和第三方模塊中大量使用。EventEmitter本質上是一個觀察者模式的實現,這種模式可...
摘要:從異步編程解決方案說起吧事件發布訂閱模式模式流程控制庫事件發布訂閱模式事件監聽器模式是一種廣泛運用于異步編程的模式,是回調函數的事件話,又稱發布訂閱模式。 從node異步編程解決方案說起吧: 事件發布/訂閱模式 Promise/deferred模式 流程控制庫 事件發布/訂閱模式 事件監聽器模式是一種廣泛運用于異步編程的模式,是回調函數的事件話,又稱發布/訂閱模式。 主要實現的幾個...
環境:Node v8.2.1; Npm v5.3.0;OS Windows10 1、 Node事件介紹 Node大多數核心 API 都采用慣用的異步事件驅動架構,其中某些類型的對象(觸發器)會周期性地觸發命名事件來調用函數對象(監聽器)。 所有能觸發事件的對象都是 EventEmitter 類的實例。 這些對象開放了一個 eventEmitter.on() 函數,允許將一個或多個函數綁定到會被對象...
摘要:為什么把叫做集合而不能稱為嚴格意義上的對象,來看這個集合的構造函數可以見得,是與處于同一層級的而非是繼承自,所以說由實例出來的對象更加的純凈,并沒有諸如等方法,更像是一個集合。 寫在前面 事件的編程方式具有輕量級、松耦合、只關注事務點等優勢,在瀏覽器端,有著自己的一套DOM事件機制,其中含包括這諸如事件冒泡,事件捕獲等;然而Node的事件機制沒有事件冒泡等,其原理就是設計模式中的觀察者...
摘要:觀察者模式觀察者模式廣泛的應用于語言中,瀏覽器事件如鼠標單擊,鍵盤事件都是該模式的例子??梢钥吹?,這就是觀察者模式的訂閱方法實現。小結通過創建可觀察的對象,當發生一個感興趣的事件時可將該事件通告給所有觀察者,從而形成松散的耦合。 觀察者模式 觀察者模式(observer)廣泛的應用于javascript語言中,瀏覽器事件(如鼠標單擊click,鍵盤事件keyDown)都是該模式的例子。...
閱讀 2034·2021-11-11 16:54
閱讀 2111·2019-08-30 15:55
閱讀 3611·2019-08-30 15:54
閱讀 391·2019-08-30 15:44
閱讀 2228·2019-08-30 10:58
閱讀 424·2019-08-26 10:30
閱讀 3048·2019-08-23 14:46
閱讀 3191·2019-08-23 13:46