注册事件监听函数

102 阅读3分钟

题目

    interface IEmitter<Event> {
      // 注册事件监听函数
      on(eventName: string, handler: (e: Event) => void): void;
      // 注册事件监听函数,仅执行一次
      once(eventName: string, handler: (e: Event) => void): void;
      // 取消注册
      off(eventName: string, handler: (e: Event) => void): void;
      // 触发事件
      emit(eventName: string, e: Event): void;
      // 过滤当前所有的事件监听函数,生成一个新的 Emitter。fn是针对入参进行过滤,入参不满足fn的事件会被过滤掉
      filter(fn: (e: Event) => boolean): IEmitter<Event>;
    }
      // write code here
    }
    

测试用例

    describe('test events', () => {
      it('用例1: on, emit, off 方法', function () {
        let i = 0;
        const emitter = new Emitter < number > ();
        const handler = (num) => {
          i += num;
        };
        emitter.on('add i', handler);
        emitter.emit('add i', 2);
        emitter.emit('add i', -1);
        expect(i).toBe(1);
        emitter.off('add i', handler);
        emitter.emit('add i', 1);
        expect(i).toBe(1);
      });

      it('用例2: once 方法', function () {
        let i = 0;
        const emitter = new Emitter < number > ();
        const handler = (num) => {
          i += num;
        };
        emitter.once('add i', handler);
        emitter.emit('add i', 2);
        emitter.emit('add i', -1);
        expect(i).toBe(2);
      });
    }

作答

// 定义一个泛型类 Emitter,它实现了特定事件类型 'Event' 的 IEmitter 接口。
class Emitter<Event> implements IEmitter<Event> {
  // 用于存储事件监听器的私有属性,使用 Map,其中事件名称作为键,处理函数数组作为值。
  private listeners: Map<string, ((e: Event) => void)[]>;

  // 构造函数,用于创建 Emitter 实例时初始化 'listeners' 映射。
  constructor() {
    this.listeners = new Map();
  }

  // 方法用于为特定事件名称注册事件监听器。
  on(eventName: string, handler: (e: Event) => void): void {
    // 检查是否没有为给定事件名称注册监听器,如果没有,创建一个空数组。
    if (!this.listeners.has(eventName)) {
      this.listeners.set(eventName, []);
    }
    // 将提供的 'handler' 函数添加到指定事件名称的事件监听器数组中。
    this.listeners.get(eventName)!.push(handler);
  }

  // 方法用于注册只会执行一次的事件监听器,仅对特定事件名称生效。
  once(eventName: string, handler: (e: Event) => void): void {
    // 创建一个新函数,用于包装提供的 'handler',在执行后自动移除自身。
    const onceHandler = (e: Event) => {
      handler(e);
      this.off(eventName, onceHandler); // 执行后移除监听器。
    };
    // 为指定事件名称注册新的 'onceHandler'。
    this.on(eventName, onceHandler);
  }

  // 方法用于移除特定事件名称下的特定事件监听器。
  off(eventName: string, handler: (e: Event) => void): void {
    // 检查是否存在给定事件名称的监听器。
    if (this.listeners.has(eventName)) {
      const handlers = this.listeners.get(eventName)!;
      // 查找提供的 'handler' 在数组中的索引,如果找到则移除。
      const index = handlers.indexOf(handler);
      if (index !== -1) {
        handlers.splice(index, 1);
      }
    }
  }

  // 方法用于触发带有特定事件名称的事件,并传递事件对象给所有已注册的监听器。
  emit(eventName: string, e: Event): void {
    // 检查是否存在给定事件名称的监听器。
    if (this.listeners.has(eventName)) {
      const handlers = this.listeners.get(eventName)!;
      // 迭代事件监听器,并调用每个关联的处理函数,传递提供的事件对象。
      for (const handler of handlers) {
        handler(e);
      }
    }
  }

  // 方法用于基于提供的过滤函数创建一个新的 Emitter 实例,其中只包括通过过滤的事件监听器。
  filter(fn: (e: Event) => boolean): IEmitter<Event> {
    // 创建一个新的 Emitter 实例来存储经过过滤的事件监听器。
    const filteredEmitter = new Emitter<Event>();
    // 遍历此实例中的现有事件监听器。
    for (const [eventName, handlers] of this.listeners) {
      // 基于提供的过滤函数过滤处理函数。
      const filteredHandlers = handlers.filter((handler) => fn);
      // 将经过过滤的处理函数注册到 filteredEmitter 中。
      for (const handler of filteredHandlers) {
        filteredEmitter.on(eventName, handler);
      }
    }
    // 返回带有经过过滤的事件监听器的 filteredEmitter。
    return filteredEmitter;
  }
}