WeakMap 是解决缓存结果又不干扰对象生命周期矛盾的理想工具,其键为弱引用,不阻止垃圾回收,无需手动清理,避免污染原始对象且天然支持对象实例唯一性。

WeakMap 是解决“缓存结果又不干扰对象生命周期”这一矛盾的理想工具。它不延长对象存活时间,也不要求修改原始对象结构,特别适合为 DOM 节点、组件实例、大型数据模型等缓存计算结果。
为什么 WeakMap 能做到“不污染原始对象”
普通缓存(如 Map 或对象)需要你显式把键和值存进去,但一旦键是某个对象,这个引用就会阻止垃圾回收——哪怕该对象在业务逻辑中早已被丢弃。WeakMap 的键是弱引用,只要原始对象没有其他强引用,它就能被自动回收,连带缓存条目也消失。你不需要调用 delete,也不用在对象销毁前手动清理。
- 不改原始对象:无需添加属性(比如 obj._cache = {...}),避免命名冲突或意外覆盖
- 不干预生命周期:对象被移除 DOM 或脱离作用域后,缓存自然失效,无内存泄漏风险
- 键天然唯一:每个对象实例都是独立键,不同组件即使结构相同也不会互相干扰
典型使用模式:为对象绑定一次计算结果
常见场景是反复调用一个开销大的函数,比如根据 DOM 元素尺寸计算布局偏移、解析复杂 JSON Schema、或生成唯一 ID 映射。你可以用 WeakMap 把输入对象和结果关联起来:
- 创建全局 WeakMap 实例(通常定义在模块顶层)
- 每次调用函数时,先用对象查缓存;命中则直接返回,未命中则计算并 set 进去
- 确保传入的 key 是对象(不能是字符串或数字),例如 element、this、options 对象等
const layoutCache = new WeakMap();
function getLayoutMetrics(element) {
if (layoutCache.has(element)) {
return layoutCache.get(element);
}
const metrics = {
width: element.offsetWidth,
height: element.offsetHeight,
offsetTop: element.offsetTop
};
layoutCache.set(element, metrics);
return metrics;
}
注意事项与边界情况
WeakMap 不是万能缓存,它有明确的设计约束,用错反而会出问题:
- 键只能是对象(null 除外),不能是字符串、数字、布尔值——如果想用 id 缓存,请先用 id 查对象,再用对象当 key
- 无法遍历或清空:没有 keys()、values()、clear() 方法,也不能 for...of,这既是限制也是保护(防误删/误读)
- 不保证立即回收:GC 时机由引擎决定,缓存可能短暂残留,但不会长期驻留
- 不适合长期稳定缓存:若需控制过期、LRU 或主动失效,应配合其他策略(如加时间戳字段 + 定期扫描,或换用 Map + 手动管理)
搭配现代框架的实用技巧
在 React、Rax 或类组件中,WeakMap 可用于缓存组件实例级的派生状态,避免重复 useMemo 计算:
- 在模块外层声明 WeakMap,作为跨渲染周期的轻量存储
- 在 useEffect 或 componentDidMount 中首次计算并缓存,后续 render 直接取用
- 配合 useCallback 返回的函数引用作 key,可缓存函数内部闭包计算结果(注意:函数本身必须是对象类型)










