[Medium] LeetCode JS 30 - Event Emitter

2024年3月3日

💎 加入 E+ 成長計畫 與超過 500+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

LeetCode 30 Days of JavaScript

本题来自 LeetCode 的 30 天 JacaScript 挑战

题目描述

设计一个 EventEmitter 类别

这个类别应该与 Node.js 或 DOM 中的 Event Target 类似,其中包含订阅事件以及发送事件两个方法。

本题要实作的两个方法如下:

  • subscribe - 此方法接受两个参数:事件名称(字串)以及一个回呼函式(callback function)。当事件被发送时,该回呼函式会被呼叫。一个事件可以注册多个监听器(listener)。当发送带有多个回呼函式的事件时,每个回呼函式都会按照订阅顺序进行呼叫。并且应返回一个结果阵列。可以假设传递给 subscribe 的回呼函式皆不相同。subscribe 方法还会回传一个带有 unsubscribe 方法的物件,该物件可以让使用者取消订阅。当呼叫它时,会从订阅列表中删除回呼函式,并返回 undefined。
  • emit - 此方法接受两个参数:事件名称(字串)以及一个可选的参数阵列,这些参数会被传递给回呼函式。如果没有订阅给定事件的回呼函式,则返回一个空阵列。否则,返回按订阅顺序存下的所有回呼函式回传的结果阵列。
// 范例
输入:
actions = ["EventEmitter", "emit", "subscribe", "subscribe", "emit"],
values = [[], ["firstEvent", "function cb1() { return 5; }"],  ["firstEvent", "function cb1() { return 6; }"], ["firstEvent"]]

输出: [[],["emitted",[]],["subscribed"],["subscribed"],["emitted",[5,6]]]

解释:
const emitter = new EventEmitter();
emitter.emit("firstEvent"); // [], 还没有 callback 被订阅
emitter.subscribe("firstEvent", function cb1() { return 5; });
emitter.subscribe("firstEvent", function cb2() { return 6; });
emitter.emit("firstEvent"); // [5, 6], 回传 cb1 与 cb2 的呼叫结果

本题解答

以下是本题的解答,详细解题思路可以在 E+ 成长计划看到。如果想练习更多题目,推荐可以到 GreatFrontEnd 上练习

解法

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

  subscribe(eventName, callback) {
    if (!this.events.has(eventName)) {
      this.events.set(eventName, []);
    }

    const events = this.events.get(eventName);
    events.push(callback);

    return {
      unsubscribe: () => {
        const index = events.indexOf(callback);
        if (index !== -1) {
          events.splice(index, 1);
        }
      },
    };
  }

  emit(eventName, args = []) {
    if (!this.events.has(eventName)) {
      return [];
    }

    const result = [];
    const events = this.events.get(eventName);

    events.forEach((callback) => {
      result.push(callback(...args));
    });

    return result;
  }
}

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們