javascript递归深拷贝需判断类型、处理循环引用、逐层复制:基本类型直接返回,函数通常原样赋值,数组/对象新建结构并递归拷贝,date/regexp用构造函数创建,weakmap缓存已拷贝对象以避免无限递归。

JavaScript 中用递归实现对象深拷贝,核心是判断数据类型、处理循环引用、逐层复制嵌套结构。
基础递归逻辑:区分类型,分别处理
深拷贝不能简单用 JSON.parse(JSON.stringify(obj)),它会丢失函数、undefined、Symbol、Date、RegExp 等类型,也无法处理循环引用。递归方案需手动识别并复制每种值:
- 基本类型(string/number/boolean/null/undefined/Symbol)直接返回,无需递归
- 函数(function)可选择原样返回(不执行)或用 eval / Function 构造(一般不推荐,有安全风险),通常直接赋值
- 数组和普通对象:新建对应结构,遍历所有自有属性,对每个值递归调用深拷贝函数
- Date、RegExp 等内置对象:用构造函数创建新实例,再设置属性(如 new Date(obj.getTime()))
处理循环引用:用 WeakMap 记录已拷贝对象
若对象存在自引用或相互引用(如 a.b = a),递归会无限进行。解决方法是在每次进入拷贝前,先查 WeakMap 是否已处理过该源对象:
- WeakMap 的键是对象引用,不会阻止垃圾回收,适合做缓存
- 如果命中缓存,直接返回之前生成的副本,打断递归链
- 首次访问时,先新建空对象(或数组),立即存入 WeakMap,再填充属性——这样后续递归遇到同一对象时能正确复用
完整可运行示例代码
以下是一个兼顾常用类型和循环引用的递归深拷贝函数:
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
let cloned;
if (obj instanceof Date) {
cloned = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
cloned = new RegExp(obj);
} else if (Array.isArray(obj)) {
cloned = [];
} else {
cloned = {};
}
cache.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], cache);
}
}
return cloned;
}
使用方式:const newObj = deepClone(originalObj);
注意事项与边界情况
递归深拷贝不是万能方案,需注意以下几点:
- Map、Set、TypedArray、BigInt 等 ES6+ 类型需额外判断和处理(例如用 new Map([...obj.entries()]))
- 不可枚举属性(如 Object.defineProperty 设置的)默认不会被 for...in 遍历,需用 Object.getOwnPropertyNames + Object.getOwnPropertySymbols 补全
- 原型链上的属性不会被复制,深拷贝只处理自有属性
- 性能上,递归过深可能触发栈溢出,超大嵌套对象建议改用迭代(栈模拟递归)或分块处理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











