WeakMap 通过弱引用键防止内存泄漏,专用于依附对象生命周期的场景;键必须为真实对象(推荐 Node 实例),需类型校验;不替代 Map,值仍需避免强引用大型对象;应封装为模块单例并提供防护接口。

WeakMap 能防止内存泄漏,关键在于它对键的弱引用机制——不参与垃圾回收计数,对象一旦失去所有强引用,就会被立即回收,对应条目自动消失。它不是“万能缓存”,而是专为“依附于对象生命周期”的场景设计。
只用真实对象作键,且必须校验类型
WeakMap 的键只能是对象,且实际使用中几乎只接受 Node 实例(如 Element、Document)或构造函数创建的普通对象实例。传入 null、undefined、字符串、数字、Symbol 或非节点对象(比如 Window、Array),虽然部分情况不报错,但会破坏语义、埋下隐患。
- DOM 场景下务必先判断:
if (node && node instanceof Node),再调用weakMap.set(node, data) - 动态插入的节点可能尚未挂载,
document.querySelector('.x')返回 null 时直接 set 会抛TypeError - 不要用 ID 字符串代替节点本身;也不要把 event.target 直接当键——需确认它确实是 Node 类型
不替代 Map,专治“强引用锁死对象”
普通 Map 或对象字面量({})对键是强引用。哪怕 DOM 元素已调用 remove()、父元素清空了 innerHTML,只要它还在 Map 里,GC 就认为它“可达”,无法回收——DevTools 中就会出现大量 detached DOM trees。
- WeakMap 不计入引用计数,节点移除且无其他强引用后,立刻可回收
- 你不需要监听
MutationObserver,也不用在节点卸载时手动map.delete() - 值(value)仍是强引用,所以别存大型对象、组件实例或未清理的定时器回调
封装成模块单例,限制接口暴露
多个 WeakMap 实例不仅浪费内存,还会导致状态分散、类型失控。生产环境应统一导出一个受控实例,并屏蔽原始操作。
立即学习“Java免费学习笔记(深入)”;
- 在模块顶层声明:
const elementState = new WeakMap(); - 暴露带防护的方法,例如:
function setElementData(el, data) {<br> if (!(el instanceof Node)) return;<br> elementState.set(el, data);<br>} - 避免把 WeakMap 引用导出或赋给全局变量,否则外部可绕过校验随意写入
典型安全用法:状态绑定与事件解耦
最自然的落地场景,是给对象附加私有状态,同时确保生命周期完全对齐。
-
DOM 状态管理:为按钮存禁用标记
elementState.set(btn, { disabled: true }),后续通过btn.isConnected判断是否还活着 -
事件监听复用:用 WeakMap 存一个 Map,记录每个节点绑定了哪些回调,避免重复
addEventListener -
构造函数私有字段:类外定义
const privateData = new WeakMap(),构造时用this作键存数据,实例方法通过privateData.get(this)访问


















