WeakMap和WeakSet的核心价值是让关联数据随对象自动回收,避免内存泄漏;WeakMap以对象为弱引用键存储键值对,适合私有状态管理;WeakSet弱引用对象集合,用于标记活跃对象或防重复处理。

WeakMap 和 WeakSet 的核心价值,是让关联数据“跟着对象一起走”——对象被回收,数据自动消失,不用手动清理。
用 WeakMap 关联私有或临时状态
当你要为某个对象附加额外信息(比如缓存、配置、内部状态),又不希望影响它的生命周期时,WeakMap 是首选。它的键必须是对象,且只弱引用该对象;一旦对象本身没了强引用,整个键值对就自动失效。
- 键只能是对象,不能是字符串、数字等基本类型
- 值可以是任意类型,但值本身仍是强引用——如果值里又反向引用了键对象,可能阻碍回收
- 适合场景:类的私有字段模拟、DOM 元素绑定元数据、函数调用缓存(以参数对象为键)
例如给每个实例加隐藏配置:
const configCache = new WeakMap();<br>class Button {<br> constructor(el) {<br> this.el = el;<br> configCache.set(this, { disabled: false, timeout: 300 });<br> }<br> get config() { return configCache.get(this); }<br>}<br>const btn = new Button(document.getElementById('submit'));<br>btn = null; // 实例被回收 → configCache 中对应条目也自动消失用 WeakSet 标记活跃对象或防重复处理
WeakSet 存储唯一对象,且只弱引用它们。它不存“值”,只回答“这个对象是否在里面”。对象一旦失去其他引用,就会自动从 WeakSet 中剔除。
- 不能遍历、没有 size、无法清空,但正因如此,它轻量且安全
- 适合场景:记录已渲染节点、跳过已处理的事件目标、避免递归遍历时重复访问同一对象
- 注意:WeakSet 不支持基本类型,也不能用作通用去重容器(如数组去重)
例如防止对同一个 DOM 节点多次初始化:
const initializedNodes = new WeakSet();<br>function initNode(node) {<br> if (initializedNodes.has(node)) return;<br> // 执行初始化逻辑<br> initializedNodes.add(node);<br>}<br>initNode(document.body);<br>document.body = null; // 节点被回收 → WeakSet 自动清理避开常见陷阱
WeakMap/WeakSet 不是万能的,误用反而引发隐性问题:
- 不要指望立即清除:垃圾回收时机不可控,条目可能短暂残留
- WeakMap 的值不是弱引用:若值中持有对键的引用(比如闭包、嵌套对象),会形成循环引用,阻止回收
- 调试困难:不能遍历、不能序列化、控制台显示为空或“WeakMap {}”,需靠 has()/get() 主动检查
- 不能替代 Map/Set 做常规数据结构:需要遍历、计数、持久化时,请用标准类型
与普通 Map/Set 对比的关键差异
根本区别在于引用强度和生命周期管理逻辑:
- Map/Set:强引用键/值 → 只要结构存在,内容就一直占内存
- WeakMap/WeakSet:仅弱引用键(WeakMap)或元素(WeakSet)→ 内存释放由 GC 驱动,无需人工 delete
- WeakMap/WeakSet 无迭代器、无 clear()、无 size 属性,设计上就是“隐形助手”,不是主数据容器
它们不是功能更少的替代品,而是面向不同问题的专用工具:一个管“谁还在”,一个管“谁配什么”。用对地方,内存泄漏和循环引用问题就能自然化解。










