weakmap 的键必须是对象,不能是字符串、数字或计算属性名;若需动态键名,应以对象为键、内部用 map 存储计算名对应数据。

计算属性名本身不能作为弱引用的键名——WeakMap 和 WeakSet 的键必须是对象,不能是字符串、数字或动态生成的属性名。所谓“计算属性名作为弱引用键名”属于概念混淆,问题根源不在垃圾回收延迟,而在于误用弱引用机制。
WeakMap 的键只能是对象,不支持字符串键
WeakMap 不接受字符串、Symbol 或计算得出的属性名作为键。它只认对象实例(包括 DOM 元素、函数、普通对象等)。
- ✅ 正确:const cache = new WeakMap(); cache.set(someElement, data);
- ❌ 错误:cache.set('userProfile', data) → 抛 TypeError
- ❌ 错误:cache.set(`key-${id}`, data) → 同样报错,因为是字符串
想用“计算名”绑定数据?换思路,别硬套 WeakMap
如果你本意是“根据某个动态标识(如 id、type)缓存对象相关数据”,但又希望该缓存不阻止对象回收,推荐以下方式:
- 用 WeakMap 存储对象 → 数据映射,键是目标对象本身,值是一个普通 Map 或 Object,再用计算名作内部键
- 例如:不要 cache.set(`item-${id}`, data),而是 cache.set(obj, new Map().set(`item-${id}`, data))
- 这样只要 obj 被回收,整条 WeakMap 条目自动消失,内部 Map 也一并释放
为什么不会引发“垃圾回收延迟”
WeakMap 的键是弱持有,GC 不会因 WeakMap 中存在该键而延长对象生命周期。所谓“延迟”通常源于误解:
- 对象仍有其他强引用(如全局变量、闭包、事件监听器)→ GC 不会回收,和 WeakMap 无关
- 你误把字符串当键,实际没存进去,后续读取不到,以为“缓存还在但没生效”
- GC 本身有非即时性(按需触发,非写完立刻回收),但这不是 WeakMap 导致的“延迟”,而是引擎正常行为
替代方案:需要字符串键时,用 Map + 手动清理
若确实需要以字符串为键、同时避免内存泄漏,可结合 WeakRef + FinalizationRegistry 主动监听对象销毁,并在 registry 回调中从 Map 中删除对应项:
- Map 存字符串 → 数据,WeakRef 持有源对象,FinalizationRegistry 注册清理逻辑
- 比 WeakMap 复杂,但可控;适合高级场景,如框架级资源管理
- 注意:FinalizationRegistry 的回调时机不确定,不可依赖其执行顺序或即时性










