javascript递归深拷贝中循环引用检测的关键是用weakmap记录已遍历对象及其拷贝结果:先查weakmap,存在则返回对应拷贝值;不存在则先占位再递归处理属性并更新。

JavaScript 中用递归实现深拷贝时,循环引用检测的关键是记录已遍历过的对象及其对应的拷贝结果,避免重复处理同一对象导致栈溢出或无限循环。
用 WeakMap 记录对象映射关系
WeakMap 是最合适的存储结构,因为它能以原对象为键,不阻止垃圾回收,且天然支持对象引用作为键:
- 每次进入递归前,先检查当前对象是否已在 WeakMap 中存在;若存在,直接返回对应拷贝值(即“已处理过”,说明遇到循环引用)
- 若不存在,先在 WeakMap 中存入 原对象 → 占位空对象/数组(提前占位,防止后续递归再次进入)
- 再递归处理其属性,完成后更新 WeakMap 中的值为最终拷贝结果
区分类型并递归处理
基础类型(string、number、boolean、null、undefined、symbol、bigint)直接返回;引用类型需进一步判断:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- Array:创建新数组,逐项递归拷贝
- Date / RegExp / Map / Set / TypedArray 等内置对象:用对应构造器实例化新对象,再填充内容
-
普通对象(plain object):用
Object.create(Object.getPrototypeOf(obj))保持原型链,再遍历自有属性递归拷贝 - 函数、Error、Promise 等不可深拷贝类型:通常直接返回原值(或按需浅拷贝/忽略)
一个精简可运行的示例
以下是一个支持循环引用检测的深拷贝函数核心逻辑:
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
let cloned;
if (Array.isArray(obj)) {
cloned = [];
} else if (obj instanceof Date) {
cloned = new Date(obj);
} else if (obj instanceof RegExp) {
cloned = new RegExp(obj);
} else {
cloned = Object.create(Object.getPrototypeOf(obj));
}
seen.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], seen);
}
}
return cloned;
}
注意事项与边界情况
实际使用中需注意:
- WeakMap 的键必须是对象,所以对原始值无需也不应存入
- Symbol 属性需用
Object.getOwnPropertySymbols()显式获取并处理 - 不可枚举属性(如某些内置属性)是否拷贝取决于需求,一般只处理自有可枚举属性
- 不能拷贝函数内部的闭包变量、this 绑定、生成器状态等,深拷贝本质是对数据结构的复制,不是“克隆运行时”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










