为什么我们要在事件发射器的订阅方法声明中声明取消订阅方法?

Why do we declare the unsubscribe method for an event emitter in its subscribe method declaration?

我在 LeetCode 上阅读了这个事件发射器的实现,想问几个问题。

  1. subscribe方法的return中有release方法有什么用?为什么我不能让它有自己的方法?
  2. 如何使用这样的 unsubscribe 方法?如果它是自己的方法,我将如何使用它?
  3. 作者说将回调放在一个对象中的原因是为了能够添加多个同名的回调。这被认为是好的做法吗?
  4. 关于如何改进此实现(可读性、结构)的任何建议?
  5. 为什么 subscriptions 变量没有在 constructor 中定义?

谢谢。

class EventEmitter {
  subscriptions = new Map()
  
  subscribe(eventName, callback) {
    if (!this.subscriptions.has(eventName)) {
      this.subscriptions.set(eventName, new Set())
    }
    const newSub = { callback }
    this.subscriptions.get(eventName).add(newSub)
    
    return {
      unsubscribe: () => {
        const evSub = this.subscriptions.get(eventName)
        evSub.delete(newSub)
        if (evSub.size === 0)
          this.subscriptions.delete(eventName)
      }
    }
  }
  
  emit(eventName, ...args) {
    const callbacks = this.subscriptions.get(eventName)
    if (!callbacks) return
    
    for (let c of callbacks) {
      c.callback(...args)
    }
  }
}
  1. 在subscribe方法的return中放release方法有什么用?为什么我不能让它有自己的方法?

答案 1: 如果取消订阅需要只能通过订阅获得的信息(如订阅 eventName),那么直接从创建中提供功能是有意义的.它使您不必以某种中间形式存储取消订阅过程中所需的数据。如果你愿意,你可以让它成为自己的方法,但仍然需要 return 它就像这段代码:

unsubscribe(eventName) => {
  const evSub = this.subscriptions.get(eventName)
  evSub.delete(newSub)
  if (evSub.size === 0)
    this.subscriptions.delete(eventName)
}

subscribe(eventName, callback) {
  if (!this.subscriptions.has(eventName)) {
    this.subscriptions.set(eventName, new Set())
  }
  const newSub = { callback }
  this.subscriptions.get(eventName).add(newSub)
    
  return () => unsubscribe(eventName);
}
  1. 如何使用这样的取消订阅方法,如果它是自己的方法,我将如何使用它?

答案 2: 您将 return 值从 subscribe 存储在某个您可以访问它的地方,然后在需要时通过调用 [=14 调用它=].无论是否在其自身的功能中,它的使用方式都没有什么不同。它仍然需要知道 eventName,因此您需要从 subscribe 编辑的 return。就像在这段代码中:

const unsub = subscribe("event1", () => {});
// then later 
unsub();  // unsubscribe from event1
  1. 作者说将回调放在一个对象中的原因是为了能够添加多个同名的回调。这被认为是好的做法吗?

答案3:没有什么问题。这是一个设计选择。

  1. 关于如何使这个实现更好(可读性、结构)有什么建议吗?

答案4:个人选择。

  1. 为什么没有在构造函数中定义订阅?

答案 5: 我假设您问的是 subscriptions 变量(复数)而不是 subscription 函数。它被定义为一个 class 变量,不需要任何特定于 ctor 参数的初始化,因此无需进行初始化。如果你愿意,你可以把它放在一个 ctor 中,但这只会让代码变长而没有任何实际好处。如果 ctor 接受了一些会影响 subscriptions 初始值的参数,那么它可以在 ctor 中完成。