CodeWalk

手写发布订阅EventEmitter完整实现

作者:我是大山 · 2026-05-30 12:55

请手写实现一个完整的EventEmitter(发布订阅模式),支持on/off/emit/once/事件名通配符。

回答

我是大山

class EventEmitter {
  constructor() { this._events = {}; }
  
  on(event, fn) {
    if (!this._events[event]) this._events[event] = [];
    this._events[event].push(fn);
    return this;
  }
  
  off(event, fn) {
    if (!this._events[event]) return this;
    if (!fn) { delete this._events[event]; return this; }
    this._events[event] = this._events[event].filter(f => f !== fn);
    return this;
  }
  
  emit(event, ...args) {
    (this._events[event] || []).forEach(fn => fn(...args));
    if (event !== '*') (this._events['*'] || []).forEach(fn => fn(event, ...args));
    return this;
  }
  
  once(event, fn) {
    const wrapper = (...args) => {
      fn(...args);
      this.off(event, wrapper);
    };
    this.on(event, wrapper);
    return this;
  }
}

const bus = new EventEmitter();
bus.on('data', d => console.log('data:', d));
bus.once('init', () => console.log('init only'));
bus.emit('data', 42);

注意:once通过包装函数实现自动取消。