weakmap 可避免深拷贝中循环引用导致的无限递归和内存泄漏,因其键为弱引用且只接受对象,能自动清理已释放对象的映射;而 map 的强引用会导致源对象无法被 gc。

WeakMap 可以在手写深拷贝中记录已处理的对象引用,避免循环引用导致的无限递归。它的核心优势在于:键是弱引用,不会阻止垃圾回收,且天然支持以对象为键——这正好匹配“按原对象实例做唯一标识”的需求。
为什么必须用 WeakMap 而不是 Map?
Map 的键是强引用,如果用它缓存源对象 → 拷贝对象的映射,会导致源对象无法被 GC 回收(尤其在长期运行的拷贝逻辑中),造成内存泄漏。WeakMap 的键是弱引用,当源对象在外部被释放时,对应的键值对会自动消失,更安全。
如何用 WeakMap 记录并查重?
在递归拷贝前,先查 WeakMap 中是否已有该源对象的拷贝结果;如果有,直接返回,跳过重复处理。
- 初始化一个 WeakMap 实例(如 const seen = new WeakMap())作为闭包变量或参数传入
- 每次进入拷贝函数时,用 seen.has(obj) 判断是否已处理过该对象
- 如果存在,直接返回 seen.get(obj)
- 如果不存在,先创建新对象(如 Array.isArray(obj) ? [] : {}),再立即 seen.set(obj, copy),再递归填充属性——顺序不能颠倒,否则子属性递归时可能再次遇到同一对象而查不到
一个精简可用的实现片段
(仅展示核心逻辑,省略类型判断和基本类型处理)
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
const copy = Array.isArray(obj) ? [] : {};
seen.set(obj, copy);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = deepClone(obj[key], seen);
}
}
return copy;
}
注意边界情况
WeakMap 只接受对象(包括函数、数组、Date、RegExp 等)作键,不能用 null、undefined、原始类型(string/number/boolean)作键——但这也恰好合理:原始类型无需防循环,直接拷贝即可;只有对象才可能形成引用环。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











