力扣刷题:事件发射器

67 阅读3分钟

前言:锻炼自己的思想,规范自己的编程思路。

问题:

设计一个 EventEmitter 类。这个接口与 Node.js 或 DOM 的 Event Target 接口相似,但有一些差异。EventEmitter 应该允许订阅事件和触发事件。

你的 EventEmitter 类应该有以下两个方法:

subscribe - 这个方法接收两个参数:一个作为字符串的事件名和一个回调函数。当事件被触发时,这个回调函数将被调用。 一个事件应该能够有多个监听器。当触发带有多个回调函数的事件时,应按照订阅的顺序依次调用每个回调函数。应返回一个结果数组。你可以假设传递给 subscribe 的回调函数都不是引用相同的。 subscribe 方法还应返回一个对象,其中包含一个 unsubscribe 方法,使用户可以取消订阅。当调用 unsubscribe 方法时,回调函数应该从订阅列表中删除,并返回 undefined。 emit - 这个方法接收两个参数:一个作为字符串的事件名和一个可选的参数数组,这些参数将传递给回调函数。如果没有订阅给定事件的回调函数,则返回一个空数组。否则,按照它们被订阅的顺序返回所有回调函数调用的结果数组。

示例:(放代码里面)

输入:actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"], values = [[], ["firstEvent", "function cb1() { return 5; }"],  ["firstEvent", "function cb1() { return 5; }"], ["firstEvent"]]
输出:[[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]
解释:
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], 还没有订阅任何回调函数
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], 返回 cb1 和 cb2 的输出

思路:

函数名为 EventEmitter。它包含一个私有字段#e,用于存储事件和回调函数之间的关系。它还包含两个方法:subscribe 和 emit。

subscribe 方法接受两个参数:event 和 cb。它检查 #e 字段中是否已经存在一个键为 event 的条目。如果不存在,它将在 #e 字段中创建一个新条目,其键为 event,值为一个空数组。然后,它将回调函数 cb 添加到该条目的值(一个数组)中。最后,该方法返回一个对象,该对象具有一个名为unsubscribe 的方法。当调用此方法时,它将从与 event 关联的条目的值(一个数组)中删除回调函数 cb。

emit 方法接受两个参数:event 和可选的 args(默认为空数组)。它检查 #e 字段中是否存在一个键为 event 的条目。如果存在,它将获取该条目的值(一个包含回调函数的数组),并对每个回调函数执行 map 操作,使用扩展运算符将args 作为参数传递给每个回调函数。最后,它返回 map 操作的结果(一个包含每个回调函数返回值的数组)。如果不存在与event 关联的条目,则返回一个空数组。

基于上述思考,代码如下:

class EventEmitter {
  #e = {}  
  subscribe(event, cb) {
    if(!this.#e[event])  this.#e[event] = []
    this.#e[event].push(cb)
    return {
        unsubscribe: () => {
            this.#e[event] = this.#e[event].filter(v => v !== cb)   
        }
    };
  }

  emit(event, args = []) {
     return (this.#e[event] ?? []).map(v => v(...args))
  }
}

执行结果如下图:

image-20230627174412836.png