[Medium] LeetCode JS 30 - Event Emitter

2024年3月3日

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

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 上追蹤我們