自定义事件

使用 CustomEvent 和 EventTarget 创建自定义事件

问题

JavaScript 中如何创建和使用自定义事件?

解答

方式一:使用 CustomEvent

// 创建自定义事件,可以携带数据
const event = new CustomEvent('userLogin', {
  detail: { userId: 123, username: 'john' },
  bubbles: true,      // 是否冒泡
  cancelable: true    // 是否可取消
});

// 监听事件
document.addEventListener('userLogin', (e) => {
  console.log('用户登录:', e.detail.username);
});

// 触发事件
document.dispatchEvent(event);

方式二:使用 EventTarget 创建事件中心

// 创建独立的事件中心
const eventBus = new EventTarget();

// 监听
eventBus.addEventListener('message', (e) => {
  console.log('收到消息:', e.detail);
});

// 触发
eventBus.dispatchEvent(new CustomEvent('message', {
  detail: { text: 'Hello' }
}));

方式三:手写事件系统

class EventEmitter {
  constructor() {
    this.events = {};
  }

  // 订阅事件
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
    return this; // 支持链式调用
  }

  // 触发事件
  emit(event, ...args) {
    const callbacks = this.events[event];
    if (callbacks) {
      callbacks.forEach(cb => cb(...args));
    }
    return this;
  }

  // 取消订阅
  off(event, callback) {
    const callbacks = this.events[event];
    if (callbacks) {
      this.events[event] = callbacks.filter(cb => cb !== callback);
    }
    return this;
  }

  // 只订阅一次
  once(event, callback) {
    const wrapper = (...args) => {
      callback(...args);
      this.off(event, wrapper);
    };
    this.on(event, wrapper);
    return this;
  }
}

// 使用示例
const emitter = new EventEmitter();

emitter.on('data', (msg) => console.log('收到:', msg));
emitter.emit('data', 'Hello World');

emitter.once('login', (user) => console.log('登录:', user));
emitter.emit('login', 'Alice'); // 输出: 登录: Alice
emitter.emit('login', 'Bob');   // 无输出,因为只订阅一次

关键点

  • CustomEvent 通过 detail 属性传递自定义数据
  • dispatchEvent 触发事件,addEventListener 监听事件
  • EventTarget 可以创建独立的事件中心,不依赖 DOM
  • 手写实现的核心是维护一个事件名到回调数组的映射
  • once 的实现思路:包装回调,执行后立即移除