参考文章:
如何写出一个惊艳面试官的深拷贝?
前端手写系列01-深拷贝的两种实现与局限
手动实现一个深拷贝
深拷贝是什么?
浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
深拷贝如何实现
需要注意的几点:
1、递归
2、判断类型
3、检查环(递归闭合无法结束)
4、忽略原型
最简单的实现:JSON.parse(JSON.stringify());
需要注意的是这样写的结果是JSON value不支持的数据类型,都拷贝不了。
简易代码:
function cloneDeep(target) {
if (target === null) return null;
if (typeof target !== 'object') return target;
const newTarget = Array.isArray(target) ? [] : {};
for (let key in target) {
//不遍历其原型链上的属性
if (target.hasOwnProperty(key)) {
newTarget[key] = cloneDeep(target[key]);
}
}
return newTarget;
};
最终代码:
function cloneDeep(target, map = new Map()) {
if (target === null) return null;
if (typeof target !== 'object') return target;
if (target.constructor === Date) return new Date(target);
if (target.constructor === RegExp) return new RegExp(target);
if (map.has(target)) return map.get(target);
const newTarget = new target.constructor();
map.set(target, newTarget);
Reflect.ownKeys(target).forEach(key => {
newTarget[key] = cloneDeep(target[key], map);
})
return newTarget;
};
- 正确处理对象内的循环引用
- 保持之前的原型链
- 正确处理
bigint
类型 - 正确处理 正则、Date 类型
- 正确处理
function
、undefined
类型 - 正确处理 key 为
symbol
类型的字段