在JavaScript中,对象是引用类型,直接赋值只会复制引用地址,修改新对象会影响原对象,因此深拷贝是开发中经常需要用到的操作。深拷贝指的是完全复制一个对象,新对象与原对象互不影响,修改任意一方都不会改变另一方的数据。

常见深拷贝方法及局限性
1. JSON序列化与反序列化
这是最简单的深拷贝方式,通过JSON.stringify()将对象转为JSON字符串,再用JSON.parse()将字符串转回对象,实现深拷贝。
// JSON序列化实现深拷贝
const originObj = {
name: 'test',
age: 20,
info: {
hobby: 'reading'
}
};
const copyObj = JSON.parse(JSON.stringify(originObj));
// 修改拷贝后的对象,不会影响原对象
copyObj.info.hobby = 'coding';
console.log(originObj.info.hobby); // 输出 reading这种方式有明显局限性:无法拷贝函数、undefined、Symbol类型的值,会丢失这些属性;无法处理循环引用的对象,会直接报错;正则、Date等特殊对象会被转为普通对象,丢失原有特性。
2. 递归手动实现深拷贝
通过递归遍历对象的所有属性,判断属性类型后分别处理,可以实现更通用的深拷贝,能处理更多特殊场景。
// 递归实现深拷贝函数
function deepClone(target, map = new WeakMap()) {
// 处理基本类型和函数,直接返回
if (typeof target !== 'object' || target === null) {
return target;
}
// 处理循环引用,如果已经拷贝过该对象,直接返回缓存的结果
if (map.has(target)) {
return map.get(target);
}
// 处理数组和对象
const result = Array.isArray(target) ? [] : {};
// 将当前对象存入缓存
map.set(target, result);
// 遍历自身可枚举属性
for (const key in target) {
if (target.hasOwnProperty(key)) {
result[key] = deepClone(target[key], map);
}
}
return result;
}
// 测试递归深拷贝
const obj1 = {
name: 'demo',
fn: function() { console.log('hello'); },
date: new Date(),
arr: [1, 2, 3]
};
// 添加循环引用
obj1.self = obj1;
const obj2 = deepClone(obj1);
console.log(obj2.fn); // 输出 [Function: fn]
console.log(obj2.date instanceof Date); // 输出 true
console.log(obj2.self === obj2); // 输出 true,循环引用处理正常这种递归方式可以处理函数、循环引用,但是对正则、Map、Set等特殊内置对象的处理还不完善,需要额外添加类型判断逻辑。
3. 考虑特殊对象类型的完善递归实现
如果需要处理更多特殊对象类型,可以在递归函数中添加对应的类型判断分支。
function completeDeepClone(target, map = new WeakMap()) {
// 基本类型和函数直接返回
if (typeof target !== 'object' || target === null) {
return target;
}
// 循环引用处理
if (map.has(target)) {
return map.get(target);
}
let result;
// 处理不同类型对象
if (target instanceof Date) {
result = new Date(target);
} else if (target instanceof RegExp) {
result = new RegExp(target.source, target.flags);
} else if (target instanceof Map) {
result = new Map();
map.set(target, result);
target.forEach((value, key) => {
result.set(completeDeepClone(key, map), completeDeepClone(value, map));
});
return result;
} else if (target instanceof Set) {
result = new Set();
map.set(target, result);
target.forEach(value => {
result.add(completeDeepClone(value, map));
});
return result;
} else {
// 数组或普通对象
result = Array.isArray(target) ? [] : {};
}
map.set(target, result);
// 遍历属性拷贝
for (const key in target) {
if (target.hasOwnProperty(key)) {
result[key] = completeDeepClone(target[key], map);
}
}
// 处理Symbol类型的键
const symbolKeys = Object.getOwnPropertySymbols(target);
for (const symKey of symbolKeys) {
result[symKey] = completeDeepClone(target[symKey], map);
}
return result;
}不同深拷贝方法的选择建议
如果拷贝的对象只包含基本类型、普通对象和数组,没有特殊类型和循环引用,优先使用JSON序列化方式,代码简洁效率高。
如果对象包含函数、循环引用,或者需要保留Date、正则等对象的类型,使用递归实现的深拷贝函数,根据实际需求选择基础递归或完善版递归。
如果项目中深拷贝场景较多,也可以直接使用成熟的第三方库如lodash的cloneDeep方法,其已经处理了各类边界场景,稳定性更高。
JavaScript深拷贝对象拷贝递归拷贝JSON序列化修改时间:2026-06-03 00:34:31