weakmap 是 javascript 原生对象,用于给 dom 元素关联私有数据以避免内存泄漏;其键必须是 node 实例,需校验存在性与类型,值的生命周期也须与节点对齐。

WeakMap 不是 HTML 的特性,而是 JavaScript 原生对象;用它给 DOM 元素关联私有数据,是目前唯一能自动避免内存泄漏的通用方案。
WeakMap.set() 传 DOM 节点时抛 TypeError: Invalid value used as weak map key
这是最常见的误用:把字符串 ID、null、undefined 或非节点对象(比如 document.querySelector('.btn') 返回 null)直接传给 set()。
-
WeakMap的键必须是真实存在的对象,且仅接受Node及其子类型(Element、Document等),不接受原始值 - 务必在
set前做存在性和类型校验:if (node && node instanceof Node) - 动态插入的节点可能尚未挂载,
document.getElementById('x')返回null时直接set就会崩
为什么 Map 或普通对象会导致 DOM 内存泄漏
现象是:节点已调用 remove() 或父元素清空了 innerHTML,但 DevTools 的 Memory 面板仍显示大量 detached DOM trees,performance.memory.usedJSHeapSize 持续上涨。
-
Map和{}对键是强引用——只要条目还在,GC 就认为该 DOM 节点“可达”,不会回收 - WeakMap 不计入引用计数,节点一旦失去所有强引用(包括从 DOM 移除 + 变量被覆盖/作用域退出),就会被 GC 清理,对应条目自动失效
- 你不需要监听
MutationObserver或手动delete,也不用担心遗漏清理时机
封装成模块单例并限制键类型是刚需
全局多个 WeakMap 实例不仅浪费,还会让状态散落、难以维护。更危险的是,若把非节点对象(如 Window、Array)误当节点传入,虽不报错,但会污染存储逻辑。
- 只导出一个模块级实例:
const elementState = new WeakMap(); - 提供带防护的接口:
function setElementState(node, data) { if (!(node instanceof Node)) return; elementState.set(node, data); } - 值(
data)本身仍需谨慎:若它被定时器、未注销的事件闭包长期持有,WeakMap 并不阻止这个值的泄漏——它只管键的弱引用
真正容易被忽略的不是怎么存,而是值的生命周期是否与节点对齐;哪怕键自动释放,若你把 elementState.get(node) 返回的对象又塞进某个长周期闭包里,泄漏照旧发生。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











