参考文章:
如何写出一个惊艳面试官的深拷贝?
前端手写系列01-深拷贝的两种实现与局限
手动实现一个深拷贝

深拷贝是什么?

浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

深拷贝如何实现

需要注意的几点:
1、递归
2、判断类型
3、检查环(递归闭合无法结束)
4、忽略原型
最简单的实现:JSON.parse(JSON.stringify());
需要注意的是这样写的结果是JSON value不支持的数据类型,都拷贝不了。
简易代码:

  1. function cloneDeep(target) {
  2. if (target === null) return null;
  3. if (typeof target !== 'object') return target;
  4. const newTarget = Array.isArray(target) ? [] : {};
  5. for (let key in target) {
  6. //不遍历其原型链上的属性
  7. if (target.hasOwnProperty(key)) {
  8. newTarget[key] = cloneDeep(target[key]);
  9. }
  10. }
  11. return newTarget;
  12. };

最终代码:

  1. function cloneDeep(target, map = new Map()) {
  2. if (target === null) return null;
  3. if (typeof target !== 'object') return target;
  4. if (target.constructor === Date) return new Date(target);
  5. if (target.constructor === RegExp) return new RegExp(target);
  6. if (map.has(target)) return map.get(target);
  7. const newTarget = new target.constructor();
  8. map.set(target, newTarget);
  9. Reflect.ownKeys(target).forEach(key => {
  10. newTarget[key] = cloneDeep(target[key], map);
  11. })
  12. return newTarget;
  13. };
  • 正确处理对象内的循环引用
  • 保持之前的原型链
  • 正确处理 bigint 类型
  • 正确处理 正则Date 类型
  • 正确处理 functionundefined 类型
  • 正确处理 key 为 symbol 类型的字段