2

如果您查看这两个polyfill示例,WeakRef它们都使用WeakMap.

但我不明白这是怎么回事。AWeakMap不持有对其values的弱引用,而是对其keys。并且这两个 polyfill 都this用作键。这意味着如果我说let weakRef = new WeakRef(targetObject)targetObject除非我扔掉,否则永远不会收集垃圾weakRef。这否定了 的全部目的WeakRef,不是吗?

在我有限的实验中,我的理论似乎是正确的。看看这个jsfiddle

此外,有谁知道确实WeakRef有效的polyfill吗?

4

2 回答 2

1

这似乎是部分 polyfill 作者误解的结果,请考虑编写错误报告。

这些方法确实保留了对对象的强引用,这是一种技术上符合要求的实现,因为程序不能仅仅因为从未观察到删除对象而认为垃圾收集器被破坏。但是,要实现这一壮举会容易得多:只需将对象存储在属性中,根本不用理会 a WeakMap

正如您所观察到的,我认为有些人天真地期望 aWeakMapvalues的引用很弱。在仔细阅读文档之前,我也曾经有过这样的误解。然而,这并不能解释为什么WeakMap自 2015 年以来一直在规范中WeakRef,而在 3 年多的时间里还没有超过提案阶段。这种幼稚的集合就像一个Mapof WeakRefs,那么为什么该语言不也暴露一个呢?

事实是,它是不可能被模仿WeakRef的,WeakMap并且已经采取了仔细的设计来做到这一点(主要是它是不可枚举的)。为什么?虽然完全有可能可靠地实现这种行为,但设计组的观点是倾向于确定性和可预测性。这个简短的段落很好地总结了它,推理和一个被认为是错误设计过去决定的例子。以下两句话直接排除了类似 a 的内容WeakRef

这意味着您不应公开任何充当弱引用的 API,例如,一旦运行垃圾收集,属性就会变为 null。JavaScript 代码中的对象和数据生命周期应该是可预测的。

考虑到这一点,在遵循 W3C TAG 指南的实现中,不仅WeakMap不能使用,甚至不能使用任何其他方法(应该回答你在评论中的最后一个问题),除非可能使用像这样的丑陋和不可靠的漏洞示例中提到的具体情况。除非委员会的意见发生根本变化,否则提案WeakRef可能永远不会真正标准化。让我们希望当前版本中包含的谨慎措辞,以及对过度内存使用和其他有趣且有充分根据的用例的呼吁最终将允许此类例外。

于 2020-11-22T12:14:22.947 回答
1

您是正确的,因为这两个 polyfill 的实现都WeakRef对目标有很强的引用。WeakMap只要其他人对其键具有强引用,A就对值具有强引用;由于 key 是WeakRef,这意味着持有 a WeakRefstrong 将阻止其所指对象的垃圾收集。这样的实现在技术上是正确的,最好的正确:这是因为无操作垃圾收集器是一个完全有效的垃圾收集器

使用的唯一原因WeakMap不是为了任何内存回收效果,而是为了封装:确保 a 的持有者WeakRef获得其引用对象的唯一方法是调用WeakRef.prototype.deref,否则无法访问它;甚至不是通过猜测属性名称。基于 ECMAScript 2021 私有字段语法的等效实现如下:

class WeakRef {
    #target;
    constructor(target) {
        this.#target = target;
    }
    deref() {
        return this.#target;
    }
}

但是,polyfill 的重点是可移植到较旧的 ECMAScript 实现,这些实现通常无法提供最新的语法。另一种方法是使deref方法成为直接保存在对象中的闭包而不是WeakRef.prototype,但这会使实现明显偏离规范。


现在,如果您想要一个WeakRef偶尔清除弱引用的实现,请查看Mathieu Hofman 的 shim;它基于早期版本的规范,最明显的是命名为FinalizationGroup. 该 shim 设法在许多环境中实现非退化WeakRef,针对每种情况使用特定于主机的 API。不过,您可以忘记在浏览器中应用它,因为浏览器不会公开此类 API。 WeakRef通常是不可填充的:API(或功能相当的东西)必须由主机作为原语公开,否则根本无法实现。

于 2021-11-15T08:22:28.270 回答