WeakRef

WeakRef 对象允许你保留对另一个对象的弱引用,而不会阻止该对象被垃圾收集。

¥A WeakRef object lets you hold a weak reference to another object, without preventing that object from getting garbage-collected.

描述

¥Description

WeakRef 对象包含对对象的弱引用,称为其目标或引用对象。对对象的弱引用是不会阻止垃圾收集器回收该对象的引用。相反,普通(或强)引用将对象保留在内存中。当一个对象不再有任何强引用时,JavaScript 引擎的垃圾收集器可能会销毁该对象并回收其内存。如果发生这种情况,你将无法再从弱引用获取对象。

¥A WeakRef object contains a weak reference to an object, which is called its target or referent. A weak reference to an object is a reference that does not prevent the object from being reclaimed by the garbage collector. In contrast, a normal (or strong) reference keeps an object in memory. When an object no longer has any strong references to it, the JavaScript engine's garbage collector may destroy the object and reclaim its memory. If that happens, you can't get the object from a weak reference anymore.

因为 非注册符号 也是垃圾可收集的,所以它们也可以用作 WeakRef 对象的目标。然而,它的用例是有限的。

¥Because non-registered symbols are also garbage collectable, they can also be used as the target of a WeakRef object. However, the use case of this is limited.

尽可能避免

¥Avoid where possible

正确使用 WeakRef 需要仔细考虑,并且最好尽可能避免。避免依赖规范未保证的任何特定行为也很重要。何时、如何以及是否发生垃圾收集取决于任何给定 JavaScript 引擎的实现。你在一个引擎中观察到的任何行为在另一个引擎、同一引擎的另一个版本中甚至在同一引擎的同一版本的情况下都可能有所不同。垃圾收集是 JavaScript 引擎实现者不断完善和改进其解决方案的一个难题。

¥Correct use of WeakRef takes careful thought, and it's best avoided if possible. It's also important to avoid relying on any specific behaviors not guaranteed by the specification. When, how, and whether garbage collection occurs is down to the implementation of any given JavaScript engine. Any behavior you observe in one engine may be different in another engine, in another version of the same engine, or even in a slightly different situation with the same version of the same engine. Garbage collection is a hard problem that JavaScript engine implementers are constantly refining and improving their solutions to.

以下是作者在介绍 WeakRefproposal 中包含的一些具体观点:

¥Here are some specific points included by the authors in the proposal that introduced WeakRef:

垃圾收集者 很复杂。如果应用或库依赖 GC 及时、可预测地清理 WeakRef 或调用终结器 [清理回调],则可能会感到失望:清理工作可能比预期晚得多,或者根本不进行。变异性的来源包括:

¥Garbage collectors are complicated. If an application or library depends on GC cleaning up a WeakRef or calling a finalizer [cleanup callback] in a timely, predictable manner, it's likely to be disappointed: the cleanup may happen much later than expected, or not at all. Sources of variability include:

  • 一个对象可能比另一个对象更快地被垃圾收集,即使它们同时变得不可访问,例如由于分代收集。
  • 垃圾收集工作可以使用增量和并发技术随着时间的推移进行分解。
  • 可以使用各种运行时启发法来平衡内存使用和响应能力。
  • JavaScript 引擎可能会保存对看起来无法访问的事物的引用(例如,在闭包或内联缓存中)。
  • 不同的 JavaScript 引擎可能会以不同的方式执行这些操作,或者同一引擎可能会在不同版本之间更改其算法。
  • 复杂的因素可能会导致对象保持活动状态的时间超出预期,例如与某些 API 一起使用。

关于 WeakRef 的注释

¥Notes on WeakRefs

  • 如果你的代码刚刚为目标对象创建了 WeakRef,或者从 WeakRefderef 方法获取了目标对象,则该目标对象将不会被回收,直到当前 JavaScript job 结束(包括在 job 处运行的任何承诺反应作业) 脚本作业结束)。也就是说,你只能在事件循环的轮次之间回收 "see" 个对象。这主要是为了避免使任何给定 JavaScript 引擎的垃圾收集器的行为在代码中变得明显 - 因为如果是这样,人们将依赖该行为编写代码,当垃圾收集器的行为发生变化时,代码就会中断。(垃圾收集是一个难题;JavaScript 引擎实现者正在不断完善和改进它的工作方式。)
  • 如果多个 WeakRef 具有相同的目标,则它们彼此一致。在其中一个上调用 deref 的结果将与在其中另一个上调用 deref 的结果匹配(在同一作业中),你不会从其中一个获得目标对象,而是从另一个获得 undefined
  • 如果 WeakRef 的目标也在 FinalizationRegistry 中,则 WeakRef 的目标会在调用与注册表关联的任何清理回调的同时或之前被清除;如果你的清理回调在对象的 WeakRef 上调用 deref,它将收到 undefined
  • 你无法更改 WeakRef 的目标,它始终只能是原始目标对象或回收该目标时的 undefined
  • 即使没有任何东西强烈持有目标,WeakRef 也可能永远不会从 deref 返回 undefined,因为垃圾收集器可能永远不会决定回收该对象。

构造函数

¥Constructor

WeakRef()

创建一个新的 WeakRef 对象。

实例属性

¥Instance properties

这些属性在 WeakRef.prototype 上定义并由所有 WeakRef 实例共享。

¥These properties are defined on WeakRef.prototype and shared by all WeakRef instances.

WeakRef.prototype.constructor Optional

创建实例对象的构造函数。对于 WeakRef 实例,初始值为 WeakRef 构造函数。

注意:该属性在规范中被标记为 "规范性 可选性",这意味着符合要求的实现可能不会公开 constructor 属性。这可以防止任意代码获取 WeakRef 构造函数并能够观察垃圾收集。然而,所有主要引擎都默认公开它。

¥Note: This property is marked as "normative optional" in the specification, which means a conforming implementation may not expose the constructor property. This prevents arbitrary code from obtaining the WeakRef constructor and being able to observe garbage collection. However, all major engines do expose it by default.

WeakRef.prototype[@@toStringTag]

@@toStringTag 属性的初始值为字符串 "WeakRef"。该属性在 Object.prototype.toString() 中使用。

实例方法

¥Instance methods

WeakRef.prototype.deref()

返回 WeakRef 对象的目标对象,如果目标对象已被回收,则返回 undefined

示例

¥Examples

使用 WeakRef 对象

¥Using a WeakRef object

此示例启动 DOM 元素中显示的计数器,当该元素不再存在时停止:

¥This example starts a counter shown in a DOM element, stopping when the element doesn't exist anymore:

js
class Counter {
  constructor(element) {
    // Remember a weak reference to the DOM element
    this.ref = new WeakRef(element);
    this.start();
  }

  start() {
    if (this.timer) {
      return;
    }

    this.count = 0;

    const tick = () => {
      // Get the element from the weak reference, if it still exists
      const element = this.ref.deref();
      if (element) {
        element.textContent = ++this.count;
      } else {
        // The element doesn't exist anymore
        console.log("The element is gone.");
        this.stop();
        this.ref = null;
      }
    };

    tick();
    this.timer = setInterval(tick, 1000);
  }

  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = 0;
    }
  }
}

const counter = new Counter(document.getElementById("counter"));
setTimeout(() => {
  document.getElementById("counter").remove();
}, 5000);

规范

Specification
ECMAScript Language Specification
# sec-weak-ref-objects

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看