weakref 本身不直接实现缓存,而是配合 map 和 finalizationregistry 构建可自动清理的缓存机制,核心是避免强引用导致内存泄漏;它弱化 value,使对象可被 gc 回收,需通过 deref() 安全读取,并搭配 finalizationregistry 清理失效键,而 key 为对象时应优先使用 weakmap。

WeakRef 本身不直接“实现缓存”,而是配合 Map 和 FinalizationRegistry 构建一种可自动清理的缓存机制,核心是避免强引用锁死对象导致内存泄漏。
用 WeakRef 包装值,避免强持有
普通 Map 缓存以 DOM 节点或大对象为 value 时,会阻止 GC 回收——只要 Map 还存着它,对象就永远驻留内存。WeakRef 把 value “弱化”:它不参与垃圾回收判定,对象仅剩 WeakRef 引用时会被立即回收。
- 创建 WeakRef 实例包装目标对象:
const ref = new WeakRef(node) - 存入 Map 时存的是 ref,不是 node:
cache.set(id, ref) - 读取时必须调用
deref(),并检查返回值是否为 undefined:const node = ref.deref(); if (node) { /* 使用 */ }
搭配 FinalizationRegistry 清理失效键
WeakRef 只解决“值能否被回收”,但 Map 中的 key 仍存在,造成键冗余。FinalizationRegistry 在对象被 GC 回收瞬间触发回调,帮你主动删掉对应缓存项。
- 注册 registry 时,传入清理逻辑(如
key => cache.delete(key)) - 对每个新缓存对象,调用
registry.register(node, key)—— 注意注册的是原始对象 node,不是 WeakRef - 当 node 被回收,registry 回调执行,key 从 cache 中移除
优先考虑 WeakMap,除非 key 不是对象
如果缓存 key 本身就是 DOM 节点、组件实例等对象,直接用 WeakMap 更简单安全:它的 key 天然弱引用,节点销毁后整条记录自动消失,无需 registry。
- ✅ 推荐:
const cache = new WeakMap(); cache.set(element, data); - ❌ 不必要复杂化:用 WeakRef + registry 去缓存以 element 为 key 的场景
- ⚠️ 真正需要 WeakRef+registry 的场景:key 是字符串/数字(比如 API ID),value 是可能被移除的 DOM 或大对象
读写时要处理 undefined 和竞态
WeakRef 的 deref() 返回值不稳定——前一秒还在,后一秒就变 undefined。不能假设“查过一次存在,下次一定还在”。
- 每次使用前都应
const val = ref.deref(); if (val === undefined) return; - 避免在 deref 后做异步操作(如 setTimeout),期间对象可能已被回收
- 若需多次访问同一对象,可先解包到局部变量:
const node = ref.deref(); if (!node) return; node.classList.add(...); node.addEventListener(...);
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











