手写 EventEmitter:深入理解发布订阅模式

作者:1024肥宅日期:2025/12/7

引言

在 JavaScript 开发中,事件驱动编程是构建可维护、可扩展应用的核心范式之一。从浏览器 DOM 事件到 Node.js 的异步 I/O,从 Vue 的组件通信到 React 的状态管理,发布订阅模式无处不在。

通过手写一个符合 Node.js EventEmitter 标准的实现,我们不仅能深入理解事件驱动架构的设计原理,还能掌握 JavaScript 中闭包、内存管理、设计模式等核心概念。更重要的是,这是面试中常见的高级题目,能体现你对JavaScript设计模式的理解深度。

本文将带你从零实现一个功能完整的EventEmitter,并探讨其在实际项目中的应用和优化策略。

一、发布订阅模式的核心概念

1.1 什么是发布订阅模式

发布订阅模式(Pub/Sub)是一种消息传递范式,消息的发送者(发布者)不会将消息直接发送给特定的接收者(订阅者),而是通过一个中间件(事件中心)进行通信。

1// 类比:报纸订阅系统
2class NewspaperSystem {
3    constructor() {
4        this.subscribers = new Map(); // 事件中心
5    }
6    
7    // 订阅(读者订阅报纸)
8    subscribe(topic, reader) {
9        if (!this.subscribers.has(topic)) {
10            this.subscribers.set(topic, []);
11        }
12        this.subscribers.get(topic).push(reader);
13    }
14    
15    // 发布(报社发布新闻)
16    publish(topic, news) {
17        if (this.subscribers.has(topic)) {
18            this.subscribers.get(topic).forEach(reader => reader(news));
19        }
20    }
21    
22    // 取消订阅(读者退订)
23    unsubscribe(topic, reader) {
24        if (this.subscribers.has(topic)) {
25            const readers = this.subscribers.get(topic);
26            const index = readers.indexOf(reader);
27            if (index > -1) {
28                readers.splice(index, 1);
29            }
30        }
31    }
32}
33
1.2 核心组件
  1. 事件中心(EventEmitter): 存储事件和回调的对应关系
  2. 发布者(Publisher): 触发事件,传递数据
  3. 订阅者(Subscriber): 监听事件,处理数据
1.3 与观察者模式的对比
特性观察者模式发布订阅模式
耦合度直接耦合通过事件中心解耦
通信方式直接调用间接通信
灵活性较低更高
典型实现Vue响应式NodeJS EventEmitter

二、基础 EventEmitter 实现

2.1 最小可行实现
1class EventEmitter {
2  constructor() {
3    // 存储事件和对应的回调函数
4    this.events = new Map();
5  }
6
7  /**
8   * 订阅事件
9   * @param {string} eventName 事件名称
10   * @param {Function} callback 回调函数
11   * @returns {Function} 取消订阅的函数
12   */
13  on(eventName, callback) {
14    if (!this.events.has(eventName)) {
15      this.events.set(eventName, []);
16    }
17    this.events.get(eventName).push(callback);
18
19    // 返回取消订阅的函数
20    return () => this.off(eventName, callback);
21  }
22
23  /**
24   * 触发事件
25   * @param {string} eventName 事件名称
26   * @param {...any} args 传递给回调函数的参数
27   */
28  emit(eventName, ...args) {
29    if (!this.events.has(eventName)) {
30      return;
31    }
32
33    const callbacks = this.events.get(eventName);
34    callbacks.forEach((callback) => {
35      try {
36        callback.apply(null, args);
37      } catch (error) {
38        console.error(`Error in event listener for ${eventName}:`, error);
39      }
40    });
41  }
42
43  /**
44   * 取消订阅
45   * @param {string} eventName 事件名称
46   * @param {Function} callback 要移除的回调函数
47   */
48  off(eventName, callback) {
49    if (!this.events.has(eventName)) {
50      return;
51    }
52
53    const callbacks = this.events.get(eventName);
54    const index = callbacks.indexOf(callback);
55
56    if (index > -1) {
57      callbacks.splice(index, 1);
58
59      // 如果没有回调函数了, 删除这个事件
60      if (callbacks.length === 0) {
61        this.events.delete(eventName);
62      }
63    }
64  }
65
66  /**
67   * 一次性订阅
68   * @param {string} eventName 事件名称
69   * @param {Function} callback 回调函数
70   */
71  once(eventName, callback) {
72    const onceCallback = (...args) => {
73      callback.apply(null, args);
74      this.off(eventName, callback);
75    };
76
77    this.on(eventName, onceCallback);
78    return () => this.off(eventName, onceCallback);
79  }
80
81  /**
82   * 移除所有事件监听器, 或指定事件的所有监听器
83   * @param {string} eventName 可选, 事件名称
84   */
85  removeAllListeners(eventName) {
86    if (eventName) {
87      this.events.delete(eventName);
88    } else {
89      this.events.clear();
90    }
91  }
92}
93
94// 基础使用示例
95const emitter = new EventEmitter();
96
97// 订阅事件
98const unsubscribe = emitter.on("message", (data) => {
99  console.log("收到消息:", data);
100});
101
102// 触发事件
103emitter.emit("message", "Hello World!"); // 输出: 收到消息: Hello World!
104
105// 取消订阅
106unsubscribe();
107
108// 再次触发,不会有输出
109emitter.emit("message", "Hello Again!");
110

三、完整的 EventEmitter 实现

3.1 支持更多特性的完整实现
1class EventEmitter {
2  constructor() {
3    // 使用 Object.create(null) 避免原型污染
4    this._events = Object.create(null);
5    this._maxListeners = 10;
6  }
7
8  // ================ 核心方法 ================
9
10  /**
11   * 添加事件监听器
12   * @param {string} eventName 事件名称
13   * @param {Function} listener 监听器函数
14   * @returns {EventEmitter} this
15   */
16  on(eventName, listener) {
17    return this._addListener(eventName, listener, false);
18  }
19
20  /**
21   * 添加事件监听器(别名)
22   */
23  addListener(eventName, listener) {
24    return this.on(eventName, listener);
25  }
26
27  /**
28   * 添加一次性事件监听器
29   * @param {string} eventName 事件名称
30   * @param {Function} listener 监听器函数
31   * @returns {EventEmitter} this
32   */
33  once(eventName, listener) {
34    return this._addListener(eventName, listener, true);
35  }
36
37  /**
38   * 触发事件
39   * @param {string} eventName 事件名称
40   * @param {...any} args 传递给监听器的参数
41   * @returns {boolean} 是否有监听器被调用
42   */
43  emit(eventName, ...args) {
44    if (!this._events[eventName]) {
45      // 如果没有 error 事件的监听器,抛出错误
46      if (eventName === "error") {
47        const error = args[0];
48        if (error instanceof Error) {
49          throw error;
50        } else {
51          throw new Error("Unhandled error event");
52        }
53      }
54      return false;
55    }
56
57    const listeners = this._events[eventName];
58    const listenersCopy = listeners.slice(); // 创建副本避免迭代时修改
59
60    let called = false;
61
62    for (const listener of listenersCopy) {
63      try {
64        // 检查是否为 once 包装函数
65        if (listener._once) {
66          // 移除原始监听器
67          this._removeListener(eventName, listener);
68        }
69
70        listener.apply(this, args);
71        called = true;
72      } catch (error) {
73        // 触发错误事件
74        if (eventName !== "error") {
75          this.emit("error", error);
76        }
77      }
78    }
79
80    return called;
81  }
82
83  /**
84   * 移除事件监听器
85   * @param {string} eventName 事件名称
86   * @param {Function} listener 要移除的监听器
87   * @returns {EventEmitter} this
88   */
89  off(eventName, listener) {
90    return this.removeListener(eventName, listener);
91  }
92
93  /**
94   * 移除事件监听器
95   * @param {string} eventName 事件名称
96   * @param {Function} listener 要移除的监听器
97   * @returns {EventEmitter} this
98   */
99  removeListener(eventName, listener) {
100    return this._removeListener(eventName, listener);
101  }
102
103  /**
104   * 移除所有事件监听器
105   * @param {string} [eventName] 可选,事件名称
106   * @returns {EventEmitter} this
107   */
108  removeAllListeners(eventName) {
109    if (eventName) {
110      delete this._events[eventName];
111    } else {
112      this._events = Object.create(null);
113    }
114    return this;
115  }
116
117  // ================ 辅助方法 ================
118
119  /**
120   * 设置最大监听器数量
121   * @param {number} n 最大监听器数量
122   * @returns {EventEmitter} this
123   */
124  setMaxListeners(n) {
125    if (typeof n !== "number" || n < 0) {
126      throw new TypeError("n must be a non-negative number");
127    }
128    this._maxListeners = n;
129    return this;
130  }
131
132  /**
133   * 获取最大监听器数量
134   * @returns {number} 最大监听器数量
135   */
136  getMaxListeners() {
137    return this._maxListeners;
138  }
139
140  /**
141   * 获取指定事件的监听器数量
142   * @param {string} eventName 事件名称
143   * @returns {number} 监听器数量
144   */
145  listenerCount(eventName) {
146    if (!this._events[eventName]) {
147      return 0;
148    }
149    return this._events[eventName].length;
150  }
151
152  /**
153   * 获取所有事件名称
154   * @returns {string[]} 事件名称数组
155   */
156  eventNames() {
157    return Object.keys(this._events);
158  }
159
160  /**
161   * 获取指定事件的所有监听器
162   * @param {string} eventName 事件名称
163   * @returns {Function[]} 监听器数组
164   */
165  listeners(eventName) {
166    if (!this._events[eventName]) {
167      return [];
168    }
169    // 返回副本,避免外部修改内部数组
170    return this._events[eventName].slice();
171  }
172
173  /**
174   * 添加监听器到数组开头
175   * @param {string} eventName 事件名称
176   * @param {Function} listener 监听器函数
177   * @returns {EventEmitter} this
178   */
179  prependListener(eventName, listener) {
180    return this._addListener(eventName, listener, false, true);
181  }
182
183  /**
184   * 添加一次性监听器到数组开头
185   * @param {string} eventName 事件名称
186   * @param {Function} listener 监听器函数
187   * @returns {EventEmitter} this
188   */
189  prependOnceListener(eventName, listener) {
190    return this._addListener(eventName, listener, true, true);
191  }
192
193  /**
194   * 内部方法:添加监听器
195   * @private
196   */
197  _addListener(eventName, listener, once = false, prepend = false) {
198    if (typeof listener !== "function") {
199      throw new TypeError("listener must be a function");
200    }
201
202    // 初始化事件数组
203    if (!this._events[eventName]) {
204      this._events[eventName] = [];
205    }
206
207    const listeners = this._events[eventName];
208
209    // 检查最大监听器限制
210    if (listeners.length >= this._maxListeners && this._maxListeners !== 0) {
211      console.warn(
212        `MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ` +
213          `${listeners.length} ${eventName} listeners added. ` +
214          `Use emitter.setMaxListeners() to increase limit`
215      );
216    }
217
218    // 如果是 once,创建包装函数
219    let listenerToAdd = listener;
220    if (once) {
221      const onceWrapper = (...args) => {
222        listener.apply(this, args);
223        // 标记为 once 包装函数
224        onceWrapper._once = true;
225      };
226      // 保存原始监听器引用,用于移除
227      onceWrapper._originalListener = listener;
228      listenerToAdd = onceWrapper;
229    }
230
231    // 添加到数组开头或结尾
232    if (prepend) {
233      listeners.unshift(listenerToAdd);
234    } else {
235      listeners.push(listenerToAdd);
236    }
237
238    return this;
239  }
240
241  /**
242   * 内部方法:移除监听器
243   * @private
244   */
245  _removeListener(eventName, listener) {
246    if (!this._events[eventName]) {
247      return this;
248    }
249
250    const listeners = this._events[eventName];
251
252    // 查找要移除的监听器
253    // 需要考虑两种情况:
254    // 1. 直接传入监听器
255    // 2. 传入 once 包装函数的原始监听器
256    let index = -1;
257
258    // 尝试直接查找
259    index = listeners.indexOf(listener);
260
261    // 如果没找到,尝试查找原始监听器
262    if (index === -1) {
263      for (let i = 0; i < listeners.length; i++) {
264        const current = listeners[i];
265        if (current._originalListener === listener) {
266          index = i;
267          break;
268        }
269      }
270    }
271
272    if (index > -1) {
273      listeners.splice(index, 1);
274
275      // 如果数组为空,删除事件
276      if (listeners.length === 0) {
277        delete this._events[eventName];
278      }
279    }
280
281    return this;
282  }
283}
284
3.2 类型安全的TypeScript版本
1type Listener = (...args: any[]) => void;
2type OnceWrapper = Listener & { _originalListener?: Listener; _once?: boolean };
3
4class EventEmitter {
5    private _events: Record<string, Listener[]> = Object.create(null);
6    private _maxListeners: number = 10;
7    
8    // 核心方法
9    on(eventName: string, listener: Listener): this {
10        return this._addListener(eventName, listener, false);
11    }
12    
13    addListener(eventName: string, listener: Listener): this {
14        return this.on(eventName, listener);
15    }
16    
17    once(eventName: string, listener: Listener): this {
18        return this._addListener(eventName, listener, true);
19    }
20    
21    emit(eventName: string, ...args: any[]): boolean {
22        const listeners = this._events[eventName];
23        if (!listeners) {
24            if (eventName === 'error') {
25                const error = args[0];
26                throw error instanceof Error ? error : new Error('Unhandled error event');
27            }
28            return false;
29        }
30        
31        const listenersCopy = listeners.slice();
32        let called = false;
33        
34        for (const listener of listenersCopy) {
35            try {
36                // 检查是否为 once 包装函数
37                const onceWrapper = listener as OnceWrapper;
38                if (onceWrapper._once) {
39                    this._removeListener(eventName, listener);
40                }
41                
42                listener.apply(this, args);
43                called = true;
44            } catch (error) {
45                if (eventName !== 'error') {
46                    this.emit('error', error);
47                }
48            }
49        }
50        
51        return called;
52    }
53    
54    off(eventName: string, listener: Listener): this {
55        return this.removeListener(eventName, listener);
56    }
57    
58    removeListener(eventName: string, listener: Listener): this {
59        return this._removeListener(eventName, listener);
60    }
61    
62    removeAllListeners(eventName?: string): this {
63        if (eventName) {
64            delete this._events[eventName];
65        } else {
66            this._events = Object.create(null);
67        }
68        return this;
69    }
70    
71    // 辅助方法
72    setMaxListeners(n: number): this {
73        if (typeof n !== 'number' || n < 0) {
74            throw new TypeError('n must be a non-negative number');
75        }
76        this._maxListeners = n;
77        return this;
78    }
79    
80    getMaxListeners(): number {
81        return this._maxListeners;
82    }
83    
84    listenerCount(eventName: string): number {
85        const listeners = this._events[eventName];
86        return listeners ? listeners.length : 0;
87    }
88    
89    eventNames(): string[] {
90        return Object.keys(this._events);
91    }
92    
93    listeners(eventName: string): Listener[] {
94        const listeners = this._events[eventName];
95        return listeners ? listeners.slice() : [];
96    }
97    
98    prependListener(eventName: string, listener: Listener): this {
99        return this._addListener(eventName, listener, false, true);
100    }
101    
102    prependOnceListener(eventName: string, listener: Listener): this {
103        return this._addListener(eventName, listener, true, true);
104    }
105    
106    // 私有方法
107    private _addListener(eventName: string, listener: Listener, once: boolean, prepend: boolean = false): this {
108        if (typeof listener !== 'function') {
109            throw new TypeError('listener must be a function');
110        }
111        
112        if (!this._events[eventName]) {
113            this._events[eventName] = [];
114        }
115        
116        const listeners = this._events[eventName];
117        
118        // 检查最大监听器限制
119        if (listeners.length >= this._maxListeners && this._maxListeners !== 0) {
120            console.warn(`MaxListenersExceededWarning for event ${eventName}`);
121        }
122        
123        let listenerToAdd: Listener = listener;
124        
125        if (once) {
126            const onceWrapper: OnceWrapper = (...args: any[]) => {
127                listener.apply(this, args);
128                onceWrapper._once = true;
129            };
130            onceWrapper._originalListener = listener;
131            listenerToAdd = onceWrapper;
132        }
133        
134        if (prepend) {
135            listeners.unshift(listenerToAdd);
136        } else {
137            listeners.push(listenerToAdd);
138        }
139        
140        return this;
141    }
142    
143    private _removeListener(eventName: string, listener: Listener): this {
144        const listeners = this._events[eventName];
145        if (!listeners) return this;
146        
147        let index = listeners.indexOf(listener);
148        
149        // 如果没找到,尝试查找原始监听器
150        if (index === -1) {
151            for (let i = 0; i < listeners.length; i++) {
152                const current = listeners[i] as OnceWrapper;
153                if (current._originalListener === listener) {
154                    index = i;
155                    break;
156                }
157            }
158        }
159        
160        if (index > -1) {
161            listeners.splice(index, 1);
162            if (listeners.length === 0) {
163                delete this._events[eventName];
164            }
165        }
166        
167        return this;
168    }
169}
170

四、测试用例

4.1 基础功能测试
1console.log("=== EventEmitter 基础功能测试 ===");
2
3const emitter = new EventEmitter();
4
5// 测试1: 基本订阅和触发
6let test1Count = 0;
7emitter.on("test1", (data) => {
8  console.log("测试1 - 收到数据:", data);
9  test1Count++;
10});
11
12emitter.emit("test1", "Hello"); // 测试1 - 收到数据: Hello
13emitter.emit("test1", "World"); // 测试1 - 收到数据: World
14console.log(`测试1 - 调用次数: ${test1Count}`); // 测试1 - 调用次数: 2
15
16// 测试2: 多个监听器
17let test2Result = [];
18emitter.on("test2", (data) => {
19  test2Result.push([`listener1: ${data}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.data.md));
20});
21emitter.on("test2", (data) => {
22  test2Result.push([`listener2: ${data.toUpperCase()}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.data.md));
23});
24
25emitter.emit("test2", "hello");
26console.log("测试2 - 多个监听器:", test2Result); // 测试2 - 多个监听器: [ 'listener1: hello', 'listener2: HELLO' ]
27
28// 测试3: 取消订阅
29let test3Count = 0;
30const test3Listener = () => {
31  test3Count++;
32  console.log("测试3 - 监听器被调用");
33};
34
35emitter.on("test3", test3Listener);
36emitter.emit("test3"); // 测试3 - 监听器被调用
37emitter.off("test3", test3Listener);
38emitter.emit("test3"); // 不调用
39console.log(`测试3 - 最终调用次数: ${test3Count}`); // 测试3 - 最终调用次数: 1
40
41// 测试4: once 方法
42let test4Count = 0;
43emitter.once("test4", () => {
44  test4Count++;
45  console.log("测试4 - once 监听器被调用");
46});
47
48emitter.emit("test4"); // 测试4 - once 监听器被调用
49emitter.emit("test4"); // 测试4 - once 监听器被调用
50emitter.emit("test4"); // 不调用
51console.log(`测试4 - once 调用次数: ${test4Count}`); // 测试4 - once 调用次数: 2
52
53// 测试5: 错误处理
54let errorCaught = false;
55emitter.on("error", (error) => {
56  errorCaught = true;
57  console.log("测试5 - 捕获到错误:", error.message);
58});
59
60emitter.on("test5", () => {
61  throw new Error("测试错误"); // 测试5 - 捕获到错误: 测试错误
62});
63
64emitter.emit("test5");
65console.log(`测试5 - 错误是否被捕获: ${errorCaught}`); // 测试5 - 错误是否被捕获: true
66
4.2 高级功能测试
1console.log("\n=== EventEmitter 高级功能测试 ===");
2
3const emitter2 = new EventEmitter();
4
5// 测试6: prependListener 方法
6let test6Order = [];
7emitter2.on("test6", () => test6Order.push("normal1"));
8emitter2.on("test6", () => test6Order.push("normal2"));
9emitter2.prependListener("test6", () => test6Order.push("prepended"));
10
11emitter2.emit("test6");
12console.log("测试6 - 监听器顺序:", test6Order);
13// 测试6 - 监听器顺序: [ 'prepended', 'normal1', 'normal2' ]
14
15// 测试7: 最大监听器限制
16emitter2.setMaxListeners(2);
17console.log(`测试7 - 最大监听器数: ${emitter2.getMaxListeners()}`); // 测试7 - 最大监听器数: 2
18
19emitter2.on("test7", () => {});
20emitter2.on("test7", () => {});
21// 第三个应该触发警告
22emitter2.on("test7", () => {});
23
24// 测试8: 获取监听器信息
25emitter2.on("test8", () => {});
26emitter2.on("test8", () => {});
27emitter2.once("test8", () => {});
28
29console.log(`测试8 - 监听器数量: ${emitter2.listenerCount("test8")}`); // 3
30console.log(`测试8 - 监听器数组长度: ${emitter2.listeners("test8").length}`); // 3
31console.log(`测试8 - 事件名称: ${emitter2.eventNames()}`); // ['test6', 'test7', 'test8']
32
33// 测试9: removeAllListeners
34emitter2.removeAllListeners("test8");
35console.log(`测试9 - 移除后监听器数量: ${emitter2.listenerCount("test8")}`); // 0
36
37// 测试10: 链式调用
38emitter2
39  .on("test10", () => console.log("测试10 - 链式调用1"))
40  .on("test10", () => console.log("测试10 - 链式调用2"))
41  .emit("test10");
42
4.3 边界情况测试
1console.log('\n=== EventEmitter 边界情况测试 ===');
2
3const emitter3 = new EventEmitter();
4
5// 测试11: 重复添加相同监听器
6let test11Count = 0;
7const test11Listener = () => test11Count++;
8
9emitter3.on('test11', test11Listener);
10emitter3.on('test11', test11Listener); // 重复添加
11
12emitter3.emit('test11');
13console.log(`测试11 - 重复监听器调用次数: ${test11Count}`); // 2
14
15// 测试12: 移除不存在的监听器
16const fakeListener = () => {};
17emitter3.off('nonexistent', fakeListener); // 应该不报错
18
19// 测试13: 触发没有监听器的事件
20const result = emitter3.emit('nonexistent');
21console.log(`测试13 - 触发无监听器事件返回值: ${result}`); // false
22
23// 测试14: 错误事件处理
24try {
25    emitter3.emit('error', new Error('未处理的错误'));
26} catch (error) {
27    console.log(`测试14 - 捕获未处理的错误: ${error.message}`);
28}
29
30// 测试15: once 监听器移除后再次触发
31let test15Count = 0;
32const test15Listener = () => {
33    test15Count++;
34    console.log(`测试15 -  ${test15Count} 次调用`);
35};
36
37const removeOnce = emitter3.once('test15', test15Listener);
38emitter3.emit('test15'); // 调用
39removeOnce(); // 手动移除
40emitter3.emit('test15'); // 不调用
41console.log(`测试15 - 最终调用次数: ${test15Count}`); // 1
42
4.4 性能测试
1console.log('\n=== EventEmitter 性能测试 ===');
2
3const performanceEmitter = new EventEmitter();
4const iterations = 100000;
5
6// 准备测试数据
7const listeners = [];
8for (let i = 0; i < 100; i++) {
9    listeners.push(() => {});
10}
11
12// 测试添加监听器的性能
13console.time('添加监听器');
14for (let i = 0; i < iterations; i++) {
15    performanceEmitter.on('performance', listeners[i % listeners.length]);
16}
17console.timeEnd('添加监听器');
18
19console.log(`添加后监听器数量: ${performanceEmitter.listenerCount('performance')}`);
20
21// 测试触发事件的性能
22console.time('触发事件');
23for (let i = 0; i < iterations; i++) {
24    performanceEmitter.emit('performance', i);
25}
26console.timeEnd('触发事件');
27
28// 测试移除监听器的性能
29console.time('移除监听器');
30for (let i = 0; i < iterations; i++) {
31    performanceEmitter.off('performance', listeners[i % listeners.length]);
32}
33console.timeEnd('移除监听器');
34
35console.log(`移除后监听器数量: ${performanceEmitter.listenerCount('performance')}`);
36

五、EventEmitter的核心原理分析

51 数据结构设计
1// EventEmitter 的核心数据结构
2class EventEmitter {
3    constructor() {
4        // 使用普通对象而不是 Map 的原因:
5        // 1.  V8 中,普通对象性能更好
6        // 2. 事件名通常是字符串,适合作为对象键
7        // 3. Object.create(null) 创建没有原型的对象,避免原型污染
8        this._events = Object.create(null);
9        
10        // 每个事件对应一个监听器数组
11        // {
12        //   'event1': [listener1, listener2, ...],
13        //   'event2': [listener3, listener4, ...]
14        // }
15    }
16}
17
5.2 once 方法的实现原理
1// once 方法的实现细节
2once(eventName, listener) {
3    // 创建包装函数
4    const onceWrapper = (...args) => {
5        // 1. 执行原始监听器
6        listener.apply(this, args);
7        
8        // 2. 标记为已执行
9        onceWrapper._once = true;
10        
11        // 3.  emit 中检测到这个标记后会移除监听器
12    };
13    
14    // 保存原始监听器引用,用于 off 方法
15    onceWrapper._originalListener = listener;
16    
17    // 添加到监听器数组
18    this._addListener(eventName, onceWrapper, false);
19    
20    return this;
21}
22
23// emit 方法中处理 once 监听器
24emit(eventName, ...args) {
25    // ...
26    for (const listener of listenersCopy) {
27        // 检查是否为 once 包装函数
28        if (listener._once) {
29            // 移除监听器
30            this._removeListener(eventName, listener);
31        }
32        // ...
33    }
34    // ...
35}
36
5.3 内存管理策略
1// 避免内存泄漏的实现
2class SafeEventEmitter extends EventEmitter {
3    constructor() {
4        super();
5        // 跟踪所有订阅,便于清理
6        this._subscriptions = new WeakMap();
7    }
8    
9    safeOn(eventName, listener, context = null) {
10        // 绑定上下文
11        const boundListener = context ? listener.bind(context) : listener;
12        
13        // 存储元数据
14        const meta = {
15            eventName,
16            originalListener: listener,
17            boundListener,
18            unsubscribe: () => this.off(eventName, boundListener)
19        };
20        
21        // 使用 WeakMap 存储,不会阻止垃圾回收
22        this._subscriptions.set(listener, meta);
23        
24        // 添加监听器
25        this.on(eventName, boundListener);
26        
27        // 返回增强的取消订阅函数
28        return () => {
29            this.off(eventName, boundListener);
30            this._subscriptions.delete(listener);
31        };
32    }
33}
34

六、常见面试题实现

6.1 实现一个简单的 EventBus
1// 全局事件总线(类似 Vue 中的 EventBus)
2class EventBus {
3    constructor() {
4        this._events = Object.create(null);
5    }
6    
7    // 单例模式
8    static getInstance() {
9        if (!EventBus._instance) {
10            EventBus._instance = new EventBus();
11        }
12        return EventBus._instance;
13    }
14    
15    $on(event, callback) {
16        if (!this._events[event]) {
17            this._events[event] = [];
18        }
19        this._events[event].push(callback);
20    }
21    
22    $emit(event, ...args) {
23        const callbacks = this._events[event];
24        if (!callbacks) return;
25        
26        // 使用 slice 创建副本,避免迭代时修改数组
27        callbacks.slice().forEach(callback => {
28            try {
29                callback(...args);
30            } catch (error) {
31                console.error(`EventBus error in ${event}:`, error);
32            }
33        });
34    }
35    
36    $off(event, callback) {
37        if (!this._events[event]) return;
38        
39        if (callback) {
40            const index = this._events[event].indexOf(callback);
41            if (index > -1) {
42                this._events[event].splice(index, 1);
43            }
44        } else {
45            delete this._events[event];
46        }
47    }
48    
49    $once(event, callback) {
50        const onceWrapper = (...args) => {
51            callback(...args);
52            this.$off(event, onceWrapper);
53        };
54        this.$on(event, onceWrapper);
55    }
56}
57
58// 使用示例
59const bus = EventBus.getInstance();
60
61// 组件 A
62bus.$on('user-login', (user) => {
63    console.log('组件A: 用户登录', user.name);
64});
65
66// 组件 B
67bus.$on('user-login', (user) => {
68    console.log('组件B: 更新用户信息', user.id);
69});
70
71// 登录成功后
72bus.$emit('user-login', { id: 1, name: 'Alice' });
73
6.2 实现带命名空间的事件系统
1class NamespacedEventEmitter {
2    constructor() {
3        this._events = Object.create(null);
4        this._separator = ':';
5    }
6    
7    // 解析事件名,支持命名空间
8    _parseEvent(eventString) {
9        const parts = eventString.split(this._separator);
10        if (parts.length === 1) {
11            return { namespace: null, event: parts[0] };
12        }
13        return { namespace: parts[0], event: parts.slice(1).join(this._separator) };
14    }
15    
16    // 生成完整的事件键
17    _getEventKey(namespace, event) {
18        return namespace ? `${namespace}${this._separator}${event}` : event;
19    }
20    
21    on(eventString, listener) {
22        const { namespace, event } = this._parseEvent(eventString);
23        const eventKey = this._getEventKey(namespace, event);
24        
25        if (!this._events[eventKey]) {
26            this._events[eventKey] = [];
27        }
28        
29        this._events[eventKey].push({
30            listener,
31            namespace,
32            event
33        });
34        
35        return () => this.off(eventString, listener);
36    }
37    
38    emit(eventString, ...args) {
39        const { namespace, event } = this._parseEvent(eventString);
40        
41        // 收集所有匹配的监听器
42        const listenersToCall = [];
43        
44        // 如果指定了命名空间,只触发该命名空间的事件
45        if (namespace) {
46            const eventKey = this._getEventKey(namespace, event);
47            if (this._events[eventKey]) {
48                listenersToCall.push(...this._events[eventKey]);
49            }
50        } else {
51            // 如果没有指定命名空间,触发所有匹配的事件
52            for (const eventKey in this._events) {
53                const listeners = this._events[eventKey];
54                for (const listenerInfo of listeners) {
55                    if (listenerInfo.event === event) {
56                        listenersToCall.push(listenerInfo);
57                    }
58                }
59            }
60        }
61        
62        // 执行监听器
63        listenersToCall.forEach(({ listener }) => {
64            try {
65                listener(...args);
66            } catch (error) {
67                console.error(`Error in ${eventString}:`, error);
68            }
69        });
70    }
71    
72    off(eventString, listener) {
73        const { namespace, event } = this._parseEvent(eventString);
74        
75        if (namespace) {
76            const eventKey = this._getEventKey(namespace, event);
77            if (this._events[eventKey]) {
78                const listeners = this._events[eventKey];
79                const index = listeners.findIndex(item => item.listener === listener);
80                if (index > -1) {
81                    listeners.splice(index, 1);
82                    if (listeners.length === 0) {
83                        delete this._events[eventKey];
84                    }
85                }
86            }
87        } else {
88            // 移除所有命名空间下的事件
89            for (const eventKey in this._events) {
90                const listeners = this._events[eventKey];
91                for (let i = listeners.length - 1; i >= 0; i--) {
92                    if (listeners[i].listener === listener && listeners[i].event === event) {
93                        listeners.splice(i, 1);
94                    }
95                }
96                if (listeners.length === 0) {
97                    delete this._events[eventKey];
98                }
99            }
100        }
101    }
102}
103
104// 使用示例
105const nsEmitter = new NamespacedEventEmitter();
106
107nsEmitter.on('user:login', () => console.log('用户模块: 登录'));
108nsEmitter.on('admin:login', () => console.log('管理员模块: 登录'));
109nsEmitter.on('login', () => console.log('全局: 登录'));
110
111nsEmitter.emit('user:login');    // 只输出: 用户模块: 登录
112nsEmitter.emit('admin:login');   // 只输出: 管理员模块: 登录
113nsEmitter.emit('login');         // 输出所有
114
6.3 实现支持异步监听器的 EventEmitter
1class AsyncEventEmitter extends EventEmitter {
2    /**
3     * 异步触发事件,等待所有监听器完成
4     */
5    async emitAsync(eventName, ...args) {
6        const listeners = this.listeners(eventName);
7        if (listeners.length === 0) {
8            return;
9        }
10        
11        // 并行执行所有监听器
12        const promises = listeners.map(async (listener) => {
13            try {
14                const result = listener(...args);
15                // 如果监听器返回 Promise,等待它完成
16                if (result && typeof result.then === 'function') {
17                    await result;
18                }
19            } catch (error) {
20                if (eventName !== 'error') {
21                    await this.emitAsync('error', error);
22                } else {
23                    throw error;
24                }
25            }
26        });
27        
28        await Promise.all(promises);
29    }
30    
31    /**
32     * 顺序执行监听器(一个接一个)
33     */
34    async emitSeries(eventName, ...args) {
35        const listeners = this.listeners(eventName);
36        
37        for (const listener of listeners) {
38            try {
39                const result = listener(...args);
40                // 如果监听器返回 Promise,等待它完成
41                if (result && typeof result.then === 'function') {
42                    await result;
43                }
44            } catch (error) {
45                if (eventName !== 'error') {
46                    await this.emitSeries('error', error);
47                } else {
48                    throw error;
49                }
50            }
51        }
52    }
53}
54
55// 使用示例
56const asyncEmitter = new AsyncEventEmitter();
57
58asyncEmitter.on('process', async (data) => {
59    await new Promise(resolve => setTimeout(resolve, 100));
60    console.log('处理完成:', data);
61});
62
63asyncEmitter.on('process', async (data) => {
64    console.log('第二个监听器:', data);
65});
66
67asyncEmitter.emitAsync('process', '测试数据');
68
6.5 实现支持优先级的 EventEmitter
1class PriorityEventEmitter {
2    constructor() {
3        this._events = Object.create(null);
4        this._defaultPriority = 0;
5    }
6    
7    on(eventName, listener, priority = this._defaultPriority) {
8        if (!this._events[eventName]) {
9            this._events[eventName] = [];
10        }
11        
12        const listeners = this._events[eventName];
13        listeners.push({ listener, priority });
14        
15        // 按优先级排序(数字越小优先级越高)
16        listeners.sort((a, b) => a.priority - b.priority);
17        
18        return () => this.off(eventName, listener);
19    }
20    
21    emit(eventName, ...args) {
22        const listeners = this._events[eventName];
23        if (!listeners) return;
24        
25        // 遍历已排序的监听器
26        for (const { listener } of listeners) {
27            try {
28                const result = listener(...args);
29                // 如果监听器返回 false,停止后续监听器的执行
30                if (result === false) {
31                    break;
32                }
33            } catch (error) {
34                console.error(`Error in ${eventName}:`, error);
35            }
36        }
37    }
38    
39    off(eventName, listener) {
40        const listeners = this._events[eventName];
41        if (!listeners) return this;
42        
43        const index = listeners.findIndex(item => item.listener === listener);
44        if (index > -1) {
45            listeners.splice(index, 1);
46            if (listeners.length === 0) {
47                delete this._events[eventName];
48            }
49        }
50        
51        return this;
52    }
53}
54
55// 使用示例
56const priorityEmitter = new PriorityEventEmitter();
57
58priorityEmitter.on('process', () => console.log('优先级 10'), 10);
59priorityEmitter.on('process', () => console.log('优先级 0'), 0);
60priorityEmitter.on('process', () => console.log('优先级 5'), 5);
61
62priorityEmitter.emit('process');
63// 输出顺序: 优先级 0, 优先级 5, 优先级 10
64

七、实际应用场景

7.1 在 Vue 中实现组件通信
1// 全局事件总线
2const EventBus = new EventEmitter();
3
4//  Vue 组件中使用
5// ComponentA.vue
6export default {
7    mounted() {
8        EventBus.on('user-updated', this.handleUserUpdate);
9    },
10    methods: {
11        handleUserUpdate(user) {
12            console.log('用户更新:', user);
13            this.user = user;
14        }
15    },
16    beforeDestroy() {
17        EventBus.off('user-updated', this.handleUserUpdate);
18    }
19};
20
21// ComponentB.vue
22export default {
23    methods: {
24        updateUser() {
25            EventBus.emit('user-updated', { id: 1, name: 'John' });
26        }
27    }
28};
29
30// 或者在 Vue 原型上添加
31Vue.prototype.$bus = new EventEmitter();
32
33// 在组件中使用
34this.$bus.on('event', handler);
35this.$bus.emit('event', data);
36this.$bus.off('event', handler);
37
7.2 在 Express 中实现事件驱动架构
1const express = require('express');
2const EventEmitter = require('./EventEmitter');
3
4class AppEvents extends EventEmitter {
5    constructor() {
6        super();
7        this.setupEvents();
8    }
9    
10    setupEvents() {
11        // 定义应用级别事件
12        this.on('user:registered', (user) => {
13            console.log('新用户注册:', user.email);
14            // 发送欢迎邮件
15            // 创建用户目录
16            // 更新统计
17        });
18        
19        this.on('order:created', (order) => {
20            console.log('新订单:', order.id);
21            // 发送确认邮件
22            // 更新库存
23            // 通知物流
24        });
25        
26        this.on('error', (error, context) => {
27            console.error('应用错误:', error.message, context);
28            // 发送错误报告
29            // 记录到监控系统
30        });
31    }
32}
33
34// 创建 Express 应用
35const appEvents = new AppEvents();
36const app = express();
37
38// 中间件:将事件发射器添加到请求对象
39app.use((req, res, next) => {
40    req.appEvents = appEvents;
41    next();
42});
43
44// 路由处理
45app.post('/register', (req, res) => {
46    const user = createUser(req.body);
47    
48    // 触发事件
49    req.appEvents.emit('user:registered', user);
50    
51    res.json({ success: true, user });
52});
53
54app.post('/order', (req, res) => {
55    const order = createOrder(req.body);
56    
57    // 触发事件
58    req.appEvents.emit('order:created', order);
59    
60    res.json({ success: true, order });
61});
62
63// 错误处理中间件
64app.use((error, req, res, next) => {
65    // 触发错误事件
66    req.appEvents.emit('error', error, {
67        url: req.url,
68        method: req.method,
69        userId: req.user?.id
70    });
71    
72    res.status(500).json({ error: 'Internal server error' });
73});
74
7.3 实现简单的状态管理
1class ObservableStore {
2    constructor(initialState = {}) {
3        this._state = initialState;
4        this._prevState = null;
5        this._emitter = new EventEmitter();
6    }
7    
8    // 获取当前状态
9    getState() {
10        return this._state;
11    }
12    
13    // 设置状态
14    setState(updates) {
15        this._prevState = { ...this._state };
16        this._state = { ...this._state, ...updates };
17        
18        // 触发状态变化事件
19        this._emitter.emit('state:changed', this._state, this._prevState);
20        
21        // 触发特定属性的变化事件
22        Object.keys(updates).forEach(key => {
23            this._emitter.emit(`state:${key}:changed`, updates[key], this._prevState[key]);
24        });
25    }
26    
27    // 订阅状态变化
28    subscribe(callback) {
29        return this._emitter.on('state:changed', callback);
30    }
31    
32    // 订阅特定状态变化
33    subscribeTo(key, callback) {
34        return this._emitter.on(`state:${key}:changed`, callback);
35    }
36    
37    // 批量更新
38    batchUpdate(updater) {
39        const updates = updater(this._state);
40        this.setState(updates);
41    }
42}
43
44// 使用示例
45const store = new ObservableStore({
46    user: null,
47    theme: 'light',
48    notifications: []
49});
50
51// 订阅状态变化
52store.subscribe((newState, oldState) => {
53    console.log('状态变化:', newState);
54});
55
56// 订阅特定状态变化
57store.subscribeTo('theme', (newTheme, oldTheme) => {
58    console.log('主题变化:', oldTheme, '->', newTheme);
59    document.body.setAttribute('data-theme', newTheme);
60});
61
62// 更新状态
63store.setState({ theme: 'dark' });
64store.setState({ user: { id: 1, name: 'Alice' } });
65
66// 批量更新
67store.batchUpdate(state => ({
68    notifications: [...state.notifications, '新消息'],
69    theme: 'dark'
70}));
71

八、性能优化和注意事项

8.1 内存泄漏预防
1// 安全的 EventEmitter,自动清理订阅
2class SafeEventEmitter extends EventEmitter {
3    constructor() {
4        super();
5        // 使用 WeakRef 跟踪组件引用
6        this._componentRefs = new WeakMap();
7    }
8    
9    // 为组件绑定事件,自动清理
10    bindToComponent(component, eventName, listener) {
11        // 创建绑定函数
12        const boundListener = listener.bind(component);
13        
14        // 添加监听器
15        this.on(eventName, boundListener);
16        
17        // 存储引用
18        if (!this._componentRefs.has(component)) {
19            this._componentRefs.set(component, []);
20        }
21        this._componentRefs.get(component).push({ eventName, listener: boundListener });
22        
23        // 返回清理函数
24        return () => {
25            this.off(eventName, boundListener);
26            const refs = this._componentRefs.get(component);
27            if (refs) {
28                const index = refs.findIndex(ref => 
29                    ref.eventName === eventName && ref.listener === boundListener
30                );
31                if (index > -1) {
32                    refs.splice(index, 1);
33                }
34            }
35        };
36    }
37    
38    // 清理组件的所有事件
39    cleanupComponent(component) {
40        const refs = this._componentRefs.get(component);
41        if (refs) {
42            refs.forEach(({ eventName, listener }) => {
43                this.off(eventName, listener);
44            });
45            this._componentRefs.delete(component);
46        }
47    }
48}
49
50// 使用示例
51class Component {
52    constructor(emitter) {
53        this.emitter = emitter;
54        this._cleanupFns = [];
55    }
56    
57    setupEvents() {
58        // 绑定事件,自动管理生命周期
59        const cleanup1 = this.emitter.bindToComponent(
60            this,
61            'data',
62            this.handleData.bind(this)
63        );
64        this._cleanupFns.push(cleanup1);
65        
66        const cleanup2 = this.emitter.bindToComponent(
67            this,
68            'error',
69            this.handleError.bind(this)
70        );
71        this._cleanupFns.push(cleanup2);
72    }
73    
74    handleData(data) {
75        console.log('处理数据:', data);
76    }
77    
78    handleError(error) {
79        console.error('处理错误:', error);
80    }
81    
82    destroy() {
83        // 清理所有事件
84        this._cleanupFns.forEach(fn => fn());
85        this._cleanupFns = [];
86        
87        // 或者使用自动清理
88        this.emitter.cleanupComponent(this);
89    }
90}
91
8.2 性能优化技巧
1// 高性能 EventEmitter
2class HighPerformanceEventEmitter {
3    constructor() {
4        // 使用空对象作为原型,避免原型链查找
5        this._events = Object.create(null);
6        // 缓存空数组,避免频繁创建
7        this._emptyArray = Object.freeze([]);
8    }
9    
10    on(eventName, listener) {
11        if (!this._events[eventName]) {
12            // 预分配数组空间
13            this._events[eventName] = [];
14        }
15        
16        this._events[eventName].push(listener);
17        return this;
18    }
19    
20    emit(eventName, ...args) {
21        // 快速路径:没有监听器
22        const listeners = this._events[eventName];
23        if (!listeners) return false;
24        
25        // 使用 for 循环而不是 forEach,性能更好
26        for (let i = 0, len = listeners.length; i < len; i++) {
27            try {
28                listeners[i].apply(this, args);
29            } catch (error) {
30                // 错误处理
31                if (eventName !== 'error') {
32                    const errorListeners = this._events.error;
33                    if (errorListeners) {
34                        // 避免递归调用
35                        for (let j = 0; j < errorListeners.length; j++) {
36                            try {
37                                errorListeners[j].call(this, error);
38                            } catch (e) {
39                                // 忽略错误处理函数中的错误
40                            }
41                        }
42                    }
43                }
44            }
45        }
46        
47        return true;
48    }
49    
50    off(eventName, listener) {
51        const listeners = this._events[eventName];
52        if (!listeners) return this;
53        
54        // 从后向前遍历,避免数组移动
55        for (let i = listeners.length - 1; i >= 0; i--) {
56            if (listeners[i] === listener) {
57                listeners.splice(i, 1);
58                break;
59            }
60        }
61        
62        // 如果没有监听器了,删除属性(让 V8 优化)
63        if (listeners.length === 0) {
64            delete this._events[eventName];
65        }
66        
67        return this;
68    }
69    
70    // 批量操作优化
71    emitMany(eventNames, ...args) {
72        const results = [];
73        for (const eventName of eventNames) {
74            results.push(this.emit(eventName, ...args));
75        }
76        return results;
77    }
78}
79
8,3 调试和监控
1// 可监控的 EventEmitter
2class MonitoredEventEmitter extends EventEmitter {
3    constructor(options = {}) {
4        super();
5        this._monitoring = {
6            enabled: options.enabled !== false,
7            emitCount: 0,
8            listenerCount: 0,
9            eventStats: new Map(),
10            errorStats: new Map(),
11            slowListeners: []
12        };
13        
14        // 性能监控阈值(毫秒)
15        this._slowThreshold = options.slowThreshold || 100;
16    }
17    
18    // 重写 emit 方法以收集监控数据
19    emit(eventName, ...args) {
20        if (!this._monitoring.enabled) {
21            return super.emit(eventName, ...args);
22        }
23        
24        this._monitoring.emitCount++;
25        
26        // 更新事件统计
27        const eventStat = this._monitoring.eventStats.get(eventName) || {
28            count: 0,
29            lastEmitted: null,
30            avgDuration: 0,
31            maxDuration: 0
32        };
33        
34        eventStat.count++;
35        eventStat.lastEmitted = new Date();
36        
37        const startTime = performance.now();
38        const result = super.emit(eventName, ...args);
39        const duration = performance.now() - startTime;
40        
41        // 更新性能统计
42        eventStat.avgDuration = 
43            (eventStat.avgDuration * (eventStat.count - 1) + duration) / eventStat.count;
44        eventStat.maxDuration = Math.max(eventStat.maxDuration, duration);
45        
46        this._monitoring.eventStats.set(eventName, eventStat);
47        
48        // 记录慢监听器
49        if (duration > this._slowThreshold) {
50            this._monitoring.slowListeners.push({
51                eventName,
52                duration,
53                timestamp: new Date(),
54                args: args.slice(0, 3) // 只记录前三个参数
55            });
56            
57            // 保持慢监听器记录的数量
58            if (this._monitoring.slowListeners.length > 100) {
59                this._monitoring.slowListeners.shift();
60            }
61        }
62        
63        return result;
64    }
65    
66    // 获取监控数据
67    getMonitoringData() {
68        return {
69            ...this._monitoring,
70            currentListeners: this._getListenersCount(),
71            eventNames: this.eventNames(),
72            timestamp: new Date()
73        };
74    }
75    
76    // 重置监控数据
77    resetMonitoring() {
78        this._monitoring = {
79            enabled: true,
80            emitCount: 0,
81            listenerCount: 0,
82            eventStats: new Map(),
83            errorStats: new Map(),
84            slowListeners: []
85        };
86    }
87    
88    // 生成监控报告
89    generateReport() {
90        const data = this.getMonitoringData();
91        
92        console.log('=== EventEmitter 监控报告 ===');
93        console.log([`运行时间: ${data.timestamp.toISOString()}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.data.md));
94        console.log([`总触发次数: ${data.emitCount}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.data.md));
95        console.log([`活跃事件数量: ${data.eventNames.length}`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.data.md));
96        
97        console.log('\n事件统计:');
98        for (const [eventName, stat] of data.eventStats) {
99            console.log(`  ${eventName}:`);
100            console.log([`    触发次数: ${stat.count}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.stat.md));
101            console.log([`    平均耗时: ${stat.avgDuration.toFixed(2)}ms`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.stat.md));
102            console.log([`    最大耗时: ${stat.maxDuration.toFixed(2)}ms`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.stat.md));
103            console.log([`    最后触发: ${stat.lastEmitted.toISOString()}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.stat.md));
104        }
105        
106        if (data.slowListeners.length > 0) {
107            console.log('\n慢监听器警告:');
108            data.slowListeners.forEach((item, index) => {
109                console.log(`  ${index + 1}. ${item.eventName} - ${item.duration.toFixed(2)}ms`);
110            });
111        }
112        
113        return data;
114    }
115    
116    // 私有方法:获取监听器计数
117    _getListenersCount() {
118        const result = {};
119        for (const eventName in this._events) {
120            result[eventName] = this._events[eventName].length;
121        }
122        return result;
123    }
124}
125

九、总结与最佳实践

9.1 核心要点总结
  1. 发布订阅模式的核心: 通过事件中心解耦发布者和订阅者
  2. EventEmitter的实现要点:
  • 使用合适的数据结构存储事件和监听器
  • 正确处理 once 监听器
  • 实现错误处理机制
  • 支持链式调用
  1. 内存管理: 及时清理监听器, 避免内存泄漏
  2. 性能考虑: 选择合适的数据结构和算法
9.2 最佳实现
  1. 命名规范:
1// 好的命名
2emitter.on('user:login', handler);
3emitter.on('order:created', handler);
4
5// 不好的命名
6emitter.on('login', handler);
7emitter.on('newOrder', handler);
8
  1. 错误处理:
1// 总是监听 error 事件
2emitter.on('error', (error) => {
3    console.error('EventEmitter error:', error);
4    // 发送到错误监控系统
5});
6
7// 或者在监听器内部处理错误
8emitter.on('data', (data) => {
9    try {
10        processData(data);
11    } catch (error) {
12        console.error('处理数据时出错:', error);
13        emitter.emit('error', error);
14    }
15});
16
  1. 资源清理:
1class Component {
2    constructor() {
3        this._cleanupFns = [];
4    }
5    
6    setupEvents() {
7        const cleanup1 = emitter.on('event1', this.handler1.bind(this));
8        this._cleanupFns.push(cleanup1);
9        
10        const cleanup2 = emitter.once('event2', this.handler2.bind(this));
11        this._cleanupFns.push(cleanup2);
12    }
13    
14    destroy() {
15        // 清理所有事件监听器
16        this._cleanupFns.forEach(fn => fn());
17        this._cleanupFns = [];
18    }
19}
20
9.3 使用建议
场景推荐方案理由
简单组件通信基础 EventEmitter轻量、简单
大型应用状态管理Observable Store结构化、可预测
异步任务协调AsyncEventEmitter更好的异步支持
性能敏感场景HighPerformanceEventEmitter优化过的实现
需要监控调试MonitoredEventEmitter内置监控功能

结语

通过手写 EventEmitter,我们不仅掌握了发布订阅模式的实现原理,更重要的是理解了事件驱动编程的核心思想。EventEmitter 虽然简单,但其设计思想在现代前端框架、Node.js 后端系统以及各种复杂应用中都有广泛的应用。

记住,好的事件系统应该:

  • ✅ 职责清晰:事件中心只负责转发消息
  • ✅ 性能优秀:高频事件触发时表现良好
  • ✅ 易于调试:有良好的监控和错误处理
  • ✅ 内存安全:避免内存泄漏和资源浪费

延伸阅读:

希望这篇经过严格测试的博客能帮助你深入理解 EventEmitter!如果有任何问题或建议,欢迎讨论交流。


手写 EventEmitter:深入理解发布订阅模式》 是转载文章,点击查看原文


相关推荐


C#小案例-->让两个线程交替打印偶数和奇数值(并发)
MM_MS2025/11/28

方法一: 编写代码实现切换逻辑 using System; using System.Threading; namespace 让两个线程交替打印偶数和奇数值_并发_ { internal class Program { // ===================== 共享资源与同步工具 ===================== // 1. 偶数线程的当前值(初始为0,每次+2,打印偶数) private static i


【鸿蒙开发案例篇】鸿蒙6.0的pdfService与pdfViewManager终极爆破
威哥爱编程2025/12/15

大家好,我是V哥。 兄弟们抄家伙!今天给大家分享用鸿蒙6.0的PDF Kit撕碎文档开发防线,全程高能代码扫射,专治各种PDF开发不服!以下基于HarmonyOS 6.0(API 21)的ArkTS实战,弹药已上膛👇 联系V哥获取 鸿蒙学习资料 💣 第一弹:pdfService——文档底层爆破术 核心能力:文档加载/编辑/转换 import { pdfService } from '@kit.PDFKit'; import { BusinessError } from '@kit.Ba


智谱年末王炸:GLM-4.7开源,这可能是给程序员最好的圣诞礼物
墨风如雪2025/12/23

2025年的年底,本以为AI圈的大战会随着节日季的到来暂时偃旗息鼓,没想到智谱AI在这个节点扔下了一枚重磅炸弹。 就在12月23日,他们正式发布并开源了GLM-4.7。这不仅仅是一次常规的版本号迭代,更像是一次针对开发者痛点的精准爆破。如果你还在为开源模型写不出能跑的代码而头疼,或者还在心疼闭源API高昂的账单,那么GLM-4.7可能正是你在等的那个破局者。 这不是参数堆砌,是实打实的“智力”升级 先说最直观的感受。过去我们用开源模型写代码,往往是“一看顿悟,一跑报错”。但这次GLM-4.7在


权限与访问控制
weixin79893765432...2026/1/2

目录 一、概念二、权限与访问控制的「能力全景图」三、前端视角的「权限控制分层模型」(核心)1、登录态层(Authentication State)2、路由层(Page Access Control)3、菜单层(Navigation Control)4、组件 / 操作层(Action Control) 四、前端权限系统的“典型数据流”五、权限模型的三种常见设计(前端必须懂)1、RBAC(基于角色)2、PBAC(基于权限点)3、ABAC(基于属性/规则) 六、重点「权限与访问控制」业务剖析


后端线上发布计划模板
uzong2026/1/10

敬畏每一行代码,敬畏每一次变更。 本模板旨在通过结构化、可验证、可回溯的方式,降低发布风险,保障系统稳定。 一、📅 发布基本信息 项目内容发布名称示例:用户中心 v2.3.0 上线发布时间2026-01-15 01:00 – 02:30发布负责人xxx协同人员xxx发布类型✅ 功能上线 / 🔁 配置变更 / 🐞 紧急修复 / ⚙️ 架构调整是否灰度发布是 / 否(若“是”,说明策略:如 5% → 20% → 100%) 二、


2026前端面试题及答案
阿芯爱编程2026/1/18

2026前端面试题及答案 HTML/CSS 部分 1. 什么是盒模型?标准盒模型和IE盒模型的区别是什么? 答案: 盒模型是CSS中用于布局的基本概念,每个元素都被表示为一个矩形盒子,由内容(content)、内边距(padding)、边框(border)和外边距(margin)组成。 区别: 标准盒模型(W3C盒子模型):width和height只包含内容(content) IE盒模型(怪异模式盒子模型):width和height包含内容(content)、内边距(padding)和边框(b

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客