日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[原] 探索 EventEmitter 在 Node.js 中的实现

發布時間:2025/4/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [原] 探索 EventEmitter 在 Node.js 中的实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

你有沒有想過,為什么瀏覽器的 div 上可以綁定多個 onclick 事件,點擊一下 div 可以觸發全部的事件,jquery 的 .on(),.off(),one() 又是如何實現的?Node.js 事件驅動的原理是怎樣的?

實際上這一切都是 EventEmitter 在背后做支持,它是 JavaScript 經典的事件驅動實現,現在我們來看下 Node.js 中是如何實現的。

本文所說的監聽事件在實現上都為函數,讀者可以認為兩者相等以方便閱讀。

因為原來的 Node 代碼量比較多,為了方便演示,作者把本文的源代碼示例中涉及數據驗證,錯誤處理的部分刪除,保留了主要內容。

準備:

  • 源碼一份:github.com/nodejs/node…

概覽 EventEmitter

內部屬性:

  • _events:用來存儲監聽事件,可以是一個事件或事件數組。
  • _eventsCount:記錄已注冊的監聽事件個數。

主要方法:

  • emitter.addListener/on(eventName, listener) 添加類型為 eventName 的監聽事件到事件數組尾部
  • emitter.prependListener(eventName, listener) 添加類型為 eventName 的監聽事件到事件數組頭部
  • emitter.emit(eventName[, ...args]) 觸發類型為 eventName 的監聽事件
  • emitter.removeListener/off(eventName, listener) 移除類型為 eventName 的監聽事件
  • emitter.once(eventName, listener) 添加類型為 eventName 的監聽事件,以后只能執行一次并刪除
  • emitter.removeAllListeners([eventName]) 移除全部類型為 eventName 的監聽事件

正文

1. 初始化 init

_events 不存在時,使用 Object.create(null) 來初始化,并把 _eventsCount 設 0。

劃重點 —— Object.create(null) 可以創建一個沒有原型的對象。

為什么要用這種方法創建對象呢?開發者這么做的目的其實還是出于性能上的考慮,因為 EventEmitter 在 Node.js 中應用廣泛,為節省服務器內存和執行速度上不必要的開銷,肯定能省則省唄。

2. 添加事件綁定 addListener

首先判斷 target 的 _events 是否存在,如果不存在則還是用 Object.create(null) 創建。

如果存在,觸發 newListener 類型的事件。然后通過 event[type] 找到已經注冊 type 類型的監聽事件/監聽事件數組,并存到 existing 中。

如果該事件值為 undefined,則把直接把要注冊的監聽事件 listener 賦給不存在的事件。否則,更新事件數組(單一的 listener 要轉為數組)。

注意 prepend 的使用,可以靈活地把 listener 添加到監聽函數數組頭部或尾部。

3. 事件添加到數組頭部 prependListener

和 addListener 類似,但是 prepend 為 true。

2. 觸發事件 emit

若 handler 不存在,直接返回 false。

若 handler 是一個函數,使用 Reflect 調用函數。如果是數組的話則遍歷數組并調用,然后返回 true。

3 移除事件綁定 removeListener

按 type 取出要刪除的監聽函數列表 list = event[type],當 list 等于要刪除的監聽函數時,_eventsCount 減一后如果為 0,直接初始化 _events,否則只刪除當前類型的監聽函數。

接著往下看,若 typeof list !== 'function' 即 list 為數組時,先確定要刪除監聽事件的位置 position,然后刪掉對應的函數。

注意:為什么不用 list.splice(postion, 1) 而要專門寫一個 spliceOne 來刪除呢?

因為這個兩參數的方法要比內置的 splice 可能快上 1.5 - 10 倍!我專門查看了下提交記錄,這個版本的方法經過幾個開發者改動過最終成為現在這個樣子。不得不佩服各路大神對開源的貢獻!至于 splice 為什么慢,我沒能查到原因,也許需要去看 v8 源碼。

4 事件只能執行一次 once

這個方法的實現有點 tricky,為了維護 fired 的狀態它用到了閉包。

其它還有一些方法,我不再多寫了,基本上原理就是這樣。有興趣的同學可以自己點擊前文的源碼鏈接查看。

下面是我抄 Node.js 的 EventEmitter 簡單代碼實現:

class EventEmitter {constructor() {this.events = {};}on(type, handler) {if (!this.events[type]) {this.events[type] = [];}this.events[type].push(handler);}off(type, handler) {if (!this.events[type]) {return;}this.events[type] = this.events[type].filter(item => item !== handler);}emit(type, ...args) {this.events[type].forEach((item) => {Reflect.apply(item, this, args);});}once(type, handler) {this.on(type, this._onceWrap(type, handler, this));}_onceWrap(type, handler, target) {const state = { fired: false, handler, type , target};const wrapFn = this._onceWrapper.bind(state);state.wrapFn = wrapFn;return wrapFn;}_onceWrapper(...args) {if (!this.fired) {this.fired = true;Reflect.apply(this.handler, this.target, args);this.target.off(this.type, this.wrapFn);}} } // 初始化 const ee = new EventEmitter();// 注冊所有事件 ee.once('wakeUp', (name) => { console.log(`${name}起來啦`); }); ee.on('eat', (name) => { console.log(`${name}吃饅頭啦`) }); ee.on('eat', (name) => { console.log(`${name}喝水啦`) }); const meetingFn = (name) => { console.log(`${name}開早會啦`) }; ee.on('work', meetingFn); ee.on('work', (name) => { console.log(`${name}碼代碼啦`) });ee.emit('wakeUp', '子非'); ee.emit('wakeUp', '子非'); // 第二次沒有觸發 ee.emit('eat', '子非'); ee.emit('work', '子非'); ee.off('work', meetingFn); // 移除開會事件 ee.emit('work', '子非'); // 再次工作輸出: 子非起來啦 子非吃饅頭啦 子非喝水啦 子非開早會啦 子非碼代碼啦 子非碼代碼啦 復制代碼

總結:

讀完 Node.js 的 EventEmitter 實現,一些細節上的處理我覺得非常棒,而設計層面上,優秀的包裝和抽象思路也讓我覺得十分經典。EventEmitter 非常重要,很多大型庫像 Webpack,Socket.io 都是基于它來實現的,對于學習 Js 的同學來說是必須掌握它的。

歡迎溝通評論和交流!!!如果這篇文章幫助到了你,麻煩給個小心心哦??????

總結

以上是生活随笔為你收集整理的[原] 探索 EventEmitter 在 Node.js 中的实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。