weakset才是存储dom节点的正确选择——它对节点是弱引用,节点移除后自动释放,避免内存泄漏;而set强引用会阻止gc,导致detached节点滞留。

直接用 Set 存 DOM 节点会引发内存泄漏,而 WeakSet 才是正确选择——它不阻止垃圾回收,节点被移除后自动释放关联记录。
为什么 Set 不能存 DOM 节点
Set 对元素是强引用。一旦把 DOM 节点放进 Set,即使该节点已从页面中 remove() 或整个父容器被清空,只要 Set 还存在,这个节点就始终“可达”,无法被 GC 回收。
- 内存快照里常能看到大量 detached DOM 节点滞留,根源就是被全局 Set 意外持有
- 这类泄漏不易察觉,长期运行的单页应用(如后台管理页)容易越来越卡
- Set 还支持遍历、
size查询等操作,进一步延长对象生命周期
WeakSet 存 DOM 节点的正确写法
WeakSet 只接受对象作为成员,且对它们是弱引用:节点脱离 DOM 后若无其他引用,GC 会一并清理 WeakSet 中的对应条目。
- 只能用
add()、has()、delete(),没有size、无法遍历——这反而是安全设计 - 适合做“标记型”用途:比如记录某个节点是否已初始化、是否被隐藏、是否绑定过事件
const initialized = new WeakSet();
function initLazyElement(el) {
if (!initialized.has(el)) {
initialized.add(el);
// 执行图片加载、动画初始化等
el.classList.add('loaded');
}
}
后续哪怕 el 被 el.remove(),initialized 中不会残留无效引用。
常见误用与边界提醒
- WeakSet 不能用于去重数组:传入基本类型(如字符串、数字)会报错;传入对象也因无法遍历,没法实现类似
[...new WeakSet(arr)]的写法 - WeakSet 不适用于需要主动查询全部标记节点的场景——它天生就不支持遍历,这是刻意为之
- 如果需存储结构化数据(如状态、配置),应选 WeakMap,而不是强行往 WeakSet 塞对象再额外挂属性
搭配 WeakMap 处理更复杂元数据
WeakSet 解决“是否做过某事”,WeakMap 解决“做过什么”。例如:
- 用 WeakMap 存每个节点的点击次数、上次交互时间、自定义配置
- key 是 DOM 节点,value 是普通对象,节点销毁时整个 key-value 对自动消失
- 避免了
el.__myData = {...}这类污染原生节点的做法
const nodeState = new WeakMap();
function markClicked(el) {
const state = nodeState.get(el) || { count: 0 };
state.count++;
nodeState.set(el, state);
}
// el 移除后,state 对象也随之可被回收Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











