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