是否可以创建不拥有 JavaScript 订阅的事件发射器?

Is it possible to create event emitter which does not own its subscriptions in JavaScript?

假设我们有一些系统模块 (M) 和事件发射器 (E),它们提供 M 所需的一些更新。 当 M 订阅 E 时,它会将回调函数 (F) 传递给 E。此时 E 将引用 F,而 F 将引用 M (E → F → M)。这意味着 M 不能因为事件发射器而被垃圾回收。但从概念上讲,当 M 在整个系统中不再被引用时,它不需要 E 的任何更新,因此 E 不应该阻止它被垃圾收集。

在今天的 JavaScript 订阅模型中,它通过返回显式处理器 (DF) 来解决,这是一个为特定订阅取消订阅的函数。但我认为这很糟糕,原因有二。首先,它打破了 JavaScript 的自然规则:当某些东西不能通过引用访问时,它就会被垃圾回收。其次,disposer 是一个额外的参考。所以,现在 DF 引用了 E,所以不仅 E 阻止了 M 的 gc,反之亦然(M → DF → E 和 E → F → M)。

看来这是一个使用弱引用的好地方。但是,我不知道如何在 WeakMap 或 WeakSet 之上构建事件发射器。 即使你将订阅放在弱容器中,你仍然需要一些硬引用来在你需要发出时将它们取出来。这完全抵消了弱容器的好处!

我最好的想法是一个假设的«支持迭代的 WeakSet»。除了通常的 API 之外:addhasdelete 这样的对象应该有 forEachWeak,这与通常的 forEach 一样有效并授予对迭代对象的临时所有权。如果用户不会将此类对象提取到其他硬引用容器(如数组)中,这不会打破引用的弱点并且仍然允许迭代。在事件发射器的情况下,这样的发射器将能够发出对订阅的更新,而无需永久引用它们。

那么,是否可以通过这种或任何其他方式构建非拥有事件发射器?

But conceptually, when M becomes unreferenced from across the system, it does not require any updates from E

这取决于。这仅在 M 完全被动时才有效。一旦您想使用 M 实际 某事,您就需要它来获取更新,无论是否有其他任何东西引用它。通过将 M 存储在全局变量 (?) 中并通过尝试获取它来取消订阅 garbage-collected (这不是确定性的)来保持订阅是非常困难的。另一方面,显式处理器清晰简单。

I cannot figuire out how to build event emitter on top of WeakMap or WeakSet.

这是不可能的。 WeakMap/WeakSet 是 ephemerons actually, they do not provide weak references.