手写一个简单的深拷贝
什么是深拷贝?
了解深拷贝之前需要对浅拷贝有个基本理解。
浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
相对应的深拷贝就是:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
深拷贝的作用
简而言之,深拷贝的作用是创建一个新的对象或数组,从而保留原始对象或数组的值,同时不与原始对象共享内存,这样原始对象的改变不会影响新的对象。常用于对象A的属性中包含了另一个对象a,为了避免内部的对象a被拷贝之后还会被新对象B修改的情况。
常规深拷贝的弊端
常规深拷贝都是用JSON.stringify()和JSON.parse()进行深拷贝。但是这个方法有几个弊端,JSON.stringify()和JSON.parse()会忽略undefined、Symbol和函数等特殊值,导致无法拷贝。并且如果尝试拷贝bigInt类型的值或者是循环引用的数据,会直接报错。
//尝试深拷贝bigInt类型
let obj={
a:123n
}
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj); //此处会报错
手写深拷贝
先来手写一个简单的深拷贝:
function deepClone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = deepClone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
这个深拷贝添加了对数组的支持,但是不支持循环引用的对象。
为了解决这个问题,我们需要借助Map对象
function deepClone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = deepClone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
可以看到成功拷贝,并且内部的对象变成了Circular类型
接下来可以用WeakMap类型替换Map类型,避免对循环引用的对象进行内存释放,但是内部的对象却没被释放的情况
function deepClone(target, map = new WeakMap()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = deepClone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
转载自:https://juejin.cn/post/7367676494976319498