WeakMap 的弱引用仅作用于键,不阻止 GC 回收孤立键对象;键需脱离所有强引用路径才可能被回收,值仍为强引用,且回收时机不可预测。

WeakMap 的“弱引用”特性不是靠你主动触发的,它本身不启动垃圾回收,也不保证即时清理。真正起作用的是 JavaScript 引擎的 GC 机制——WeakMap 只是让键对象“不拦着 GC”。识别这个特性,关键看三点:键是否被其他地方强引用、GC 是否实际发生、以及你能否观测到条目消失。
键对象必须“孤立”才能被回收
WeakMap 的键只有在完全脱离所有强引用路径时,才可能被回收。所谓“强引用路径”,包括全局变量、闭包中保存的变量、DOM 树中的节点、数组元素、对象属性等。
- 如果
const el = document.getElementById('box'),又做了weakMap.set(el, data),但之后没执行el = null或移除 DOM 节点,那 el 仍被强引用,GC 不会动它 - 即使你把 el 设为 null,只要还有其他变量(比如
anotherRef = el)或事件监听器(el.addEventListener(...))持有着它,它依然不可回收 - WeakMap 自己这条引用链,不计入“可达性判断”——这是它和普通 Map 最本质的区别
回收时机由引擎决定,无法预测或强制
WeakMap 条目不会在键变成“孤立”后立刻消失。它依赖真实 GC 周期,而这个周期不受 JS 代码控制:
- V8 中,Minor GC(处理新生代)或 Major GC(全堆扫描)运行后,才可能清理掉已回收键对应的条目
- 你调用
weakMap.get(obj)返回undefined,不能断定 obj 已被回收——可能只是还没 GC,也可能 obj 从未进过 WeakMap - 没有
weakMap.size、weakMap.keys()等方法,正是因为条目状态不稳定,遍历结果不可靠
值仍是强引用,容易形成隐式循环
WeakMap 的“弱”只作用于键;它的值是强引用。这点常被忽略,却直接导致回收失败:
- 如果值是一个函数,且该函数闭包里捕获了键对象(例如
weakMap.set(el, () => el.style.color = 'red')),那就形成了循环引用:WeakMap ← el ← 函数 ← WeakMap - 如果值是一个对象,而这个对象又通过属性、方法或事件处理器反向持有键,同样会阻止键回收
- 值本身若被全局数组、定时器、另一个 Map 持有,即使键被回收,值仍驻留内存——WeakMap 不管值的生命周期
验证弱引用是否生效的实用方式
没法实时监控,但可通过间接方式观察行为是否符合弱引用预期:
- 在 Chrome DevTools 的 Memory 面板中录制堆快照,反复操作(如创建/销毁 DOM 元素 + set/get WeakMap),查看对应对象是否随 DOM 节点一起消失
- 用
performance.memory(如果可用)或任务管理器粗略观察长期运行后的内存趋势,对比用 Map 和 WeakMap 的差异 - 写一个测试:创建对象 → 存入 WeakMap → 切断所有外部引用 → 手动触发 GC(DevTools → More Tools → Rendering → 勾选 “Disable cache” + 强制垃圾回收按钮)→ 再 get,看是否返回 undefined










