使用Map以对象为键会导致内存泄漏,因其强引用阻止GC回收已失效的DOM元素或组件实例;应改用WeakMap,它对键弱引用,对象无其他引用时自动被回收。

直接用 Map 以引用类型(如对象、函数、DOM 元素)作键,**无法防止内存泄漏**——因为 Map 对键是强引用,只要该对象还在 Map 里,垃圾回收器(GC)就永远不会释放它,哪怕这个对象在业务逻辑中早已“失效”或被 DOM 移除。
为什么 Map 的对象键会导致内存泄漏
当一个 DOM 元素或组件实例被移除,但你仍把它作为 Map 的键保存着私有数据(比如缓存计算结果、绑定事件状态),这个对象就因 Map 的强持有而无法被 GC 回收。久而久之,大量“游离但存活”的对象堆积在内存中。
- 例如:
const cache = new Map(); cache.set(domEl, { lastScrollTop: 100 });—— 即使domEl.remove(),只要cache还持有它,domEl就不会被回收 -
Map支持任意键类型,也支持遍历(keys()/entries())、有size、可clear(),这些便利性是以牺牲自动内存管理为代价的
正确做法:改用 WeakMap
WeakMap 是专为这类场景设计的替代方案。它的键必须是对象,且对键采用弱引用——这意味着:一旦该对象在其他地方不再被任何强引用持有,GC 就能立即回收它,WeakMap 中对应的条目也会自动消失。
- ✅ 键只能是对象(含函数、数组、DOM 元素等),原始值(字符串、数字)会报错
- ✅ 不阻止 GC:DOM 元素被移除后,若无其他引用,它和对应 value 都会被清理
- ✅ 不可枚举:没有
keys()、values()、entries(),也没有size或clear() - ✅ 接口简洁:仅提供
set(key, value)、get(key)、has(key)、delete(key)
示例:
const elementState = new WeakMap();const btn = document.getElementById('submit');
elementState.set(btn, { disabledAt: Date.now(), attempts: 0 });
// 后续 btn.remove() → btn 对象可被 GC,elementState 中条目自动失效
WeakMap 的典型适用场景
它不是 Map 的“升级版”,而是语义不同的工具,适合生命周期需与目标对象严格对齐的用途:
- 为 DOM 元素附加私有元数据(如加载状态、校验结果、动画标记)
- 在类中模拟私有字段(配合闭包或模块级 WeakMap,避免 this 上暴露属性)
-
缓存计算结果,且缓存应随输入对象消亡而失效(如
calculateLayout(el)的结果绑定到el) - 避免闭包长期持有大对象(把外部引用存进 WeakMap,而非直接闭包捕获)
不能用 WeakMap?那得手动管理
如果你确实需要遍历、统计数量、或支持非对象键(比如字符串 ID),就不能依赖 WeakMap。此时必须主动清理:
- 在 DOM 元素卸载时显式调用
map.delete(el) - 在 React/Vue 组件
useEffect清理函数或beforeUnmount中同步清理Map条目 - 使用唯一符号(
Symbol)或字符串 ID 代替真实对象作键,再通过 ID 映射回对象(但需自行维护 ID 生命周期)


















