Symbols as WeakMap keys S4
中文标题:Symbols 作为 WeakMap 键
- 阶段: Stage 4
- 状态: 已完成
- ECMAScript 版本: ES2023
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案扩展了 WeakMap,允许使用唯一的 Symbol 作为键,解决了仅能将对象作为键的限制。它支持了更轻松的键创建、ShadowRealms 中基于膜的虚拟化以及从 Records 和 Tuples 引用对象等用例。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
Symbols 作为 WeakMap 键
阶段 4
联合作者/冠军:
- Robin Ricard (@rricard)
- Rick Button (@rickbutton)
- Daniel Ehrenberg (@littledan)
- Leo Balter (@leobalter)
- Caridy Patiño (@caridy)
- Rick Waldron (@rwaldron)
- Ashley Claymore (@acutmore)
介绍
本提案扩展了 WeakMap API,允许使用唯一的 Symbol 作为键。
目前,WeakMap 仅限于将对象作为键,这对 WeakMap 来说是一个限制,因为其目标是拥有可以被最终回收的唯一值。
Symbol 是 ECMAScript 中唯一允许唯一值的原始类型。一个 symbol 值——比如通过调用 Symbol( [ description] ) 表达式产生的值——只能通过访问其原始产生式来识别。相同表达式的任何复制——使用相同的 description 值——都不会恢复任何先前产生式的原始值。这就是为什么我们说 symbol 值是独特的。
对象被用作 WeakMap 的键,因为它们共享相同的身份方面。对象的身份只能通过访问原始产生式来验证,没有任何新对象能匹配预先存在的对象——例如——在严格比较中。
早期讨论
参见关于 Symbols 作为 WeakMap 键的早期讨论。
草案 PR
参见当前包含建议规范的 ECMA-262 草案 PR。
用例
易于创建和共享键
无需创建一个仅用作键的新对象,symbol 将为 WeakMap 的人体工程学及其键和映射项的正确角色提供更清晰的说明。
ShadowRealms、Membranes 和虚拟化
ShadowRealms 提案 不允许访问对象值。对于大多数虚拟化场景,膜系统建立在与 Realms 相关的 API 之上,使用 WeakMaps 连接引用。作为原始值的 Symbol 值仍然可访问,允许膜通过使用连接的身份来构建适当的弱映射。
Record 和 Tuple
本提案旨在解决 Record & Tuple 提案 引入的问题域;我们如何在原始值中引用和访问非原始值?
简而言之,鉴于目前讨论中提出的所有约束,我们认为通过 WeakMaps 解引用的 Symbols 是从 Records 和 Tuples 引用对象的合理解析方式。
关于这些应如何确切工作,以及有效的人体工程学/生态系统协调问题,我们希望能在 TC39 阶段进程中解决/验证。我们将从理解问题域开始,包括在没有此功能的情况下 Records 和 Tuples 为何是良好的第一步。然后,我们将探讨各种可能的解决方案及其优缺点。
Records & Tuples 不能包含对象、函数或方法,当有人尝试这样做时会抛出 TypeError:
这个限制存在是因为 Record & Tuple 提案 的关键目标之一是默认具有深度不可变保证和结构相等性。
下面提到的用户态解决方案提供了多种绕过此限制的方法,而无需额外的对象装箱语言支持,Record and Tuple 依然是可行且有用的。本提案试图描述补充这些用户态解决方案与 Record and Tuple 结合的方案,但不是将 Record and Tuple 引入语言的先决条件。
接受 Symbol 值作为 WeakMap 键将允许 JavaScript 库实现自己的类 RefCollection 功能,这些功能可重用(避免到处传递映射,使用单个全局映射,仅传递 Records and Tuples),同时不会随时间泄漏内存。
作为 WeakMap 键的知名和注册 Symbols
一些 TC39 代表在两个方向上都强烈争论。我们认为“允许”和“不允许”都是可接受的选项。
注册 Symbols
在 issue 21 中讨论了不允许注册 symbols。
知名 Symbols
允许知名 symbols 似乎并不那么糟糕,因为它们类似于在 Realm 生命周期内保持活跃的对象。在只要 JS 运行就保持活跃的 Realm 上下文(例如,在 Web 上,Worker 的 Realm)中,像 Symbol.iterator 这样的东西类似于 Object.prototype 和 Array.prototype 这样的原始元素。仅仅因为这些会保持活跃并不意味着我们禁止它们作为 WeakMap 键。
虽然可以使用 Symbol.keyFor 检测“注册” symbols,但目前没有内置谓词来测试一个 symbol 是否为“知名”。如果“知名” symbols 不允许作为 WeakMap 键,代码需要确保处理这种潜在的中止完成。
在 WeakRefs 和 FinalizationRegistry 中支持 Symbols
我们还应该在 WeakRefs 和 FinalizationRegistry 中支持 Symbols。这不仅与 Objects 作为 WeakMap 键一致,还允许用户态构建/演示更高级的功能。例如,支持以 Records/Tuples 为键的 WeakMaps。
总结
我们认为将 Symbols 作为 WeakMap 键是一个有用、最小的原语,使 Records 和 Tuples 能够引用对象,同时尊重在单个 Realm 内支持基于膜隔离的目标所施加的约束。同时,用户态解决方案似乎满足了许多/大多数用例;我们相信 Records 和 Tuples 在没有任何额外的从原始值引用对象的机制的情况下非常有用,因此独立于本提案推进 Records 和 Tuples 是有意义的。