WeakRef

WeakRef 对象允许您持有对另一个对象的弱引用,而不会阻止该对象被垃圾回收。

描述

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

因为未注册的符号也是可被垃圾回收的,所以它们也可以用作 WeakRef 对象的目标。但是,这种情况的用例有限。

尽可能避免

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

以下是引入 WeakRef提案中作者包含的一些具体要点

垃圾回收器 非常复杂。如果应用程序或库依赖于 GC 清理 WeakRef 或及时、可预测地调用终结器 [清理回调],则很可能会失望:清理可能比预期晚得多,或者根本不会发生。可变性的来源包括

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

关于 WeakRef 的说明

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

构造函数

WeakRef()

创建一个新的 WeakRef 对象。

实例属性

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

WeakRef.prototype.constructor 可选

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

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

WeakRef.prototype[Symbol.toStringTag]

[Symbol.toStringTag] 属性的初始值为字符串 "WeakRef"。此属性用于 Object.prototype.toString()

实例方法

WeakRef.prototype.deref()

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

示例

使用 WeakRef 对象

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

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);

规范

规范
ECMAScript 语言规范
# sec-weak-ref-objects

浏览器兼容性

BCD 表格仅在启用 JavaScript 的浏览器中加载。

另请参阅