成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

EventEmitter 的核心功能實現

開發 前端
EventEmitter 是 Nodejs 環境下才能使用的庫,所以不能直接用于瀏覽器環境的開發。所以我考慮自己實現一套邏輯,自己定制的話也容易根據實際情況的變動做修改。

大家好,我是前端西瓜哥。

EventEmitter 是頻率較高的前端面試題。

EventEmitter 是 Nodejs 環境下才能使用的庫,所以不能直接用于瀏覽器環境的開發。所以我考慮自己實現一套邏輯,自己定制的話也容易根據實際情況的變動做修改。

因此我決定了解一下 EventEmitter 的 API,并嘗試自己實現一套邏輯。

Nodejs 的 EventEmitter API

首先當然是要了解需求,即 EventEmitter 的 API 使用。詳細使用方式請查閱 官方文檔,我這里只簡單敘述一些常用的 API。

const { EventEmitter, errorMonitor } = require('events');
// 創建事件觸發器實例
const emitter = new EventEmitter()
// on:注冊監聽者函數。可以注冊多個監聽函數,
// 觸發事件后,會依次同步執行,順序為綁定時的順序。
// 別名為:addListener
emitter.on('event', function(a, b) => {
console.log('event emit!', a, b)
})
// once:注冊一個只會被執行一次的函數
emitter.once('event', function() => {
console.log('event emit only once!')
})
// emit:觸發事件,可提供參數。
// 如果有對應監聽器函數,會返回 true,否則返回 false
emitter.emit('event', 3, 4)
// 比較特別的是,如果沒有注冊 error 事件的監聽者,
// 觸發 error 時,錯誤不會被捕獲而直接報錯;
// 若注冊,則錯誤會被捕獲
emitter.emit('error', new Error('whoops!'))
// event.errorMonitor 是一個 Symbol
// 能夠在觸發 error 事件時,先執行被綁定的監視器函數
emitter.on(errorMonitor, err => {
console.log('error monitor')
});
// 移除指定監聽器,別名為:removeListener
emitter.off(eventName, handler)
// 獲取注冊的事件名的數組形式
emitter.eventNames()
  • 監聽者函數的 this 會指向 EventEmitter 實例。當然你可以使用各種方法修改 this 的指向,如箭頭函數或 bind 方法。
  • 每次添加監聽器時,都會觸發 newListener 事件,傳入的參數為事件名(eventName)和監聽器函數(listener)。
  • 同樣,移除監聽器時,會觸發 removeListener 事件。
  • emitter.prependListener():同 on,但會添加到監聽器數組的開頭。
  • ...

API 很多,但我不打算實現了這么多,就只實現最常用的 on、emit、off。

實現

首先,我們知道不同的事件是有特定的 eventName(事件名)的,通過指定 eventName,我們才能綁定對應的多個監聽器(函數),才能觸發事件執行綁定的這些監聽器。

這時候,我們就涉及到數據結構與算法的存儲問題了。因為結構和算法是相輔相成的,選擇不同的數據結構,使用的算法就會不同。

不同的數據結構與算法的優點的缺陷各不相同,比如空間復雜度上或時間復雜度上的效率不同。

listener 函數的存儲

那么如何存儲呢?常見的方法是使用哈希表,因為時間復雜度是 O(1),空間復雜度一般也不會太大。JavaScript 的對象本質上就是哈希表。所以我們的存儲方式是:

this.hashMap = {
'event1': [listener1, listenr2],
'event2': [],
}

一些可擴展的點:

  • 哈希表的一個問題是:無序。可以通過額外使用一個數組來記錄添加 eventName 的記錄順序。這樣的話,實現 emitter.eventNames() 可以拿到有序的事件數據。當然這樣的需求比較少見,這里只是簡單提一下。
  • 如果要實現 once(設置執行一次就不再執行的監聽器函數),則需要對函數標記,這時候可以考慮讓數組元素的格式改為 { listener: Listener, once: boolean },在觸發事件的時候,執行監聽器函數時,將 once 值為 true 的監聽器從數組中移除。
  • 可以改為鏈表實現存儲,這樣移除中間監聽器時,時間復雜度可以變成 O(1)。另外數組刪除元素的時間復雜度是 O(n)。但會引入實現上的復雜度,因為沒有內置的鏈表實現,需要自己手動實現一個沒有 BUG 的鏈表類。

on() 的實現

on() 的實現,其實就是將監聽器函數綁定到指定事件對應的數組中。實現起來并不難,只要注意如果是第一次添加指定事件時,要先初始化一個空數組即可。on 最后返回了 this,是為了實現鏈式調用。

class EventEmiter {
on(eventName, listener) {
if (!this.hashMap[eventName]) {
this.hashMap[eventName] = []
}
this.hashMap[eventName].push(listener)
return this
}
}

off() 的實現

off() 會根據傳入的事件名,找到對應的監聽器數組,從中移除指定監聽器。同樣為了實現鏈式調用返回了 this。

class EventEmiter {
off(eventName, listener) {
const listeners = this.hashMap[eventName]
if (listeners && listeners.length > 0) {
const index = listeners.indexOf(listener)
if (index > -1) {
listeners.splice(index, 1)
}
}
return this
}
}

emit() 的實現

emit() 的實現很簡單,找到事件對應的監聽器,傳入參數依次執行。如果事件沒有綁定監聽器,返回 false。否則,返回 true。

class EventEmiter {
emit(eventName, ...args) {
const listeners = this.hashMap[eventName]
if (!listeners || listeners.length === 0) return false
listeners.forEach(listener => {
listener(...args)
})
return true
}
}

完整實現

雖然很突然,我這里給出的是 TypeScript 實現,只要將類型聲明去掉就是 JavaScript 實現了。當然下面代碼是做了簡單的單元測試的,大概是沒問題的。

源碼地址:

type EventName = string | symbol
type Listener = (...args: any[]) => void
class EventEmiter {
private hashMap: { [eventName: string]: Array<Listener> } = {}
on(eventName: EventName, listener: Listener): this {
const name = eventName as string
if (!this.hashMap[name]) {
this.hashMap[name] = []
}
this.hashMap[name].push(listener)
return this
}
emit(eventName: EventName, ...args: any[]): boolean {
const listeners = this.hashMap[eventName as string]
if (!listeners || listeners.length === 0) return false
listeners.forEach(listener => {
listener(...args)
})
return true
}
off(eventName: EventName, listener: Listener): this {
const listeners = this.hashMap[eventName as string]
if (listeners && listeners.length > 0) {
const index = listeners.indexOf(listener)
if (index > -1) {
listeners.splice(index, 1)
}
}
return this
}
}

因為對象不支持 Symbol 作為索引,所以這里的實現做了類型的強轉。未來,TypeScript 可能會允許對象索引為 Symbol,Enum 等,但目前不行。

責任編輯:姜華 來源: 今日頭條
相關推薦

2023-08-04 14:31:43

Python核心項目

2009-11-18 13:11:29

PHP核心

2010-01-27 17:38:58

Windows Emb

2024-02-29 07:48:55

Python編程語言上下文管理器

2010-02-07 14:16:57

2024-04-23 00:00:00

SpringBoot監聽器

2010-04-07 09:31:06

2023-03-01 08:15:10

NginxNacos

2024-08-01 08:45:17

2010-09-22 15:31:05

OracleSPARCSolaris

2011-05-26 17:19:05

中間件

2023-10-10 14:56:27

物聯網智能建筑智能樓宇

2024-11-12 10:57:14

NumPyPython

2013-02-28 14:52:29

VMware

2011-03-07 09:55:56

2020-11-11 08:04:34

低代碼

2019-08-15 10:29:35

物聯網卡物聯網平臺

2023-02-23 08:15:33

Spring異常處理機制

2021-07-14 11:07:56

AOPJDKCglib

2024-03-07 10:40:41

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 激情视频一区 | 国产探花在线精品一区二区 | 久久爱一区 | 无码国模国产在线观看 | 操操日 | 美日韩视频 | 在线第一页 | 在线精品一区二区三区 | 一级毛片在线播放 | 国产精品观看 | 91成人在线视频 | 久久av一区 | 国产精品国产a级 | 91在线视频观看 | 日韩一级免费 | av在线一区二区 | 免费观看av| 中文字幕在线精品 | 亚洲精品自拍 | 国产成人免费视频 | 日韩欧美手机在线 | 久久久久久久久久一区 | 国产日韩欧美91 | 最新av在线网址 | 中文字幕第二十页 | 在线视频一区二区三区 | 久久精品免费 | 日韩精品一区二区三区视频播放 | 视频一区二区三区中文字幕 | 美女爽到呻吟久久久久 | 国产精品久久精品 | 午夜视频一区二区 | www.久草| 久久久久久久一区二区 | 亚洲国产精品一区 | 国产免费一区二区三区最新6 | 久久国产成人午夜av影院武则天 | 国产高清视频一区 | 日韩视频一区在线观看 | 天天碰夜夜操 | 欧美久久一级特黄毛片 |