深拷贝实现方法
先写一个最简单的深拷贝函数
function deepClone(obj) {
// 如果是基本数据类型或 null,直接返回
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 如果是数组
if (Array.isArray(obj)) {
const arrCopy = [];
for (let i = 0; i < obj.length; i++) {
arrCopy[i] = deepClone(obj[i]);
}
return arrCopy;
}
// 如果是对象
const objCopy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key]);
}
}
return objCopy;
}
// 测试
const original = {
a: 1,
b: [2, 3, { d: 4 }],
c: { e: 5 }
};
const copied = deepClone(original);
console.log(copied);
这里面看似没啥毛病,但是来了,如果对象中存在自身引用(即对象内部某个属性指向对象本身),那么普通的递归深拷贝就会进入无限循环,导致栈溢出。
为了解决这个问题,我们可以使用一个辅助对象来追踪已经拷贝过的对象,并在遇到相同的引用时直接返回已拷贝的对象。下面是改进版的深拷贝函数,能够处理自身引用的情况:
function deepClone(obj, map = new WeakMap()) {
// 如果是基本数据类型或 null,直接返回
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 如果已经拷贝过这个对象,直接返回
if (map.has(obj)) {
return map.get(obj);
}
// 如果是数组
if (Array.isArray(obj)) {
const arrCopy = [];
map.set(obj, arrCopy); // 记录这个数组
for (let i = 0; i < obj.length; i++) {
arrCopy[i] = deepClone(obj[i], map);
}
return arrCopy;
}
// 如果是对象
const objCopy = {};
map.set(obj, objCopy); // 记录这个对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key], map);
}
}
return objCopy;
}
// 测试自身引用
const obj = {};
obj.self = obj; // 自身引用
const copiedObj = deepClone(obj);
console.log(copiedObj); // 输出: { self: [Circular] }