Map因强引用键导致内存泄漏,WeakMap通过弱引用键使对象可被自动回收。DOM节点或组件卸载后,Map仍持有其引用而阻止GC,WeakMap则在对象无其他强引用时自动清理对应条目。

直接说结论:Map 本身不会泄漏,但用它缓存对象(尤其是 DOM 节点、组件实例等)时,会因强引用键阻止垃圾回收,造成泄漏;WeakMap 通过弱引用键让对象在无其他引用时自动被回收,从而从根源上切断泄漏链。
为什么 Map 容易引发内存泄漏
Map 的键是强引用——只要它还存着某个对象(比如一个按钮元素),哪怕这个按钮早已从页面中 remove(),GC 仍认为它是“可达的”,无法释放。
- DOM 节点被移除后,仍被 Map 键持有 → 节点本体 + 关联数据全部滞留内存
- 组件实例卸载后,Map 还记着它的引用 → 响应式依赖、事件监听器、闭包变量全锁死
- 全局缓存长期存活,键对象生命周期与缓存生命周期不一致 → “缓存没清,对象也清不掉”
WeakMap 如何自然切断泄漏链
WeakMap 的键只起“临时绑定”作用:它不参与 GC 的可达性判断。只要对象在别处没了强引用,哪怕 WeakMap 里还存着它,GC 也会立刻回收该对象,对应条目自动消失。
- 键必须是对象(不能是字符串/数字),天然适配 DOM、class 实例等实体
- 没有
size、不能for...of遍历、不提供keys()—— 这不是缺陷,而是弱引用不可枚举的必然设计 - API 极简:
set(key, value)、get(key)、has(key)、delete(key),语义清晰,无副作用
典型修复场景与写法对比
以“给按钮记录点击次数”为例:
-
Map 写法(泄漏风险):
const clickCount = new Map();<br>button.addEventListener('click', () => {<br> clickCount.set(button, (clickCount.get(button) || 0) + 1);<br>});<br>// button.remove() 后,clickCount 仍持引用 → 泄漏 -
WeakMap 写法(自动清理):
const clickCount = new WeakMap();<br>button.addEventListener('click', () => {<br> clickCount.set(button, (clickCount.get(button) || 0) + 1);<br>});<br>// button.remove() 后,无其他引用 → GC 回收按钮,WeakMap 条目自动失效
排查与验证的关键动作
不要只改代码,要验证是否真解决了泄漏:
- 用 Chrome DevTools Memory 面板,抓操作前、操作后、强制 GC 后三张堆快照,切到 Comparison 视图,重点看
HTMLButtonElement、HTMLDivElement等构造函数的 New 数量是否不再持续增长 - 关注
Detached DOM tree大小是否回落 —— WeakMap 修复后,这类节点应随 GC 显著减少 - 检查
Retainers链:原来挂在Map.entries下的对象,现在应不再出现在任何强引用路径中











