weakmap 和 weakref 仅作用于 js 堆对象,不直接管理 dom 或宿主资源内存;weakmap 适合挂载 dom 私有状态因其键弱引用、自动清理;weakref 适用于缓存元数据而非持有节点;webgl 纹理等需手动释放;间接强引用仍需主动清理。

WeakMap 和 WeakRef 不是用来“管理 HTML 内存”的工具,它们只作用于 JavaScript 堆中的对象,对 DOM 节点本身不释放内存,但能防止因 JS 层强引用导致的 DOM 节点无法被回收——这是你真正需要控制的泄漏点。
WeakMap 为什么适合给 DOM 节点挂私有状态
DOM 节点是对象,天然可作 WeakMap 的键;只要节点从 DOM 树移除且 JS 中无其他强引用,它就能被 GC 回收,连带 WeakMap 中对应的键值对自动消失。
- 必须用模块级单例
WeakMap,不能每次调用都new WeakMap() - 键只能是对象(
document.getElementById('x')✅,'x'❌) - 不要频繁
set(el, {})替换整个值对象——新对象没被任何变量持有,可能立即失联 - 读取前务必
nodeState.has(el)判断,get()返回undefined是正常行为,不是 bug - 常见泄漏:在事件回调里闭包捕获
el、把它塞进全局数组、或用setTimeout长期持有
WeakRef 不该用来 hold DOM 节点,但可以 hold 元数据
WeakRef 对 DOM 节点本身意义不大——节点本就支持被 WeakMap 弱持有;它的价值在于临时缓存计算结果、配置或轻量元数据,且允许这些数据随主对象一起消失。
- 典型误用:
const ref = new WeakRef(el);→ 完全多余,el本身已可被 GC - 合理用法:缓存某个节点的解析结果(如
el.getAttribute('data-config')解析后的对象),用WeakRef包一层,避免强持原始节点 -
deref()可能返回undefined,每次使用前必须检查 - 不能遍历
WeakRef,也不能监听它何时失效——GC 时机不可控
WebGL / Canvas 纹理等宿主资源完全不受 WeakRef 影响
WebGLTexture、ImageBitmap、AudioContext 这类对象是浏览器宿主环境提供的句柄,不在 JS 引擎 GC 管理范围内。WeakRef 无法包裹它们,强行调用会静默失败或抛出 TypeError。
- 想释放 GPU 纹理?必须手动调用
gl.deleteTexture(texture) - 想关联纹理生命周期?可用
WeakMap<htmlimageelement webgltexture></htmlimageelement>,因为HTMLImageElement是 JS 对象,可弱持有 - 缓存结构应基于 ID 或 URL 字符串,而不是纹理对象本身:
Map<string width: number height:></string> -
FinalizationRegistry回调里只能拿到你注册时传入的holdings(比如 id),拿不到 texture,因此无法触发deleteTexture
最易被忽略的一点:WeakMap 和 WeakRef 都不解决「间接强引用」。哪怕你用了 WeakMap,只要在某个闭包、定时器、全局数组或未清理的事件监听器里还拿着节点引用,它就永远不会被回收——工具只是辅助,清理逻辑得你自己写清楚。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











