深拷贝与浅拷贝的区别?如何实现一个深拷贝?

1. 浅拷贝是什么?规则?

浅拷贝指创建新数据,这个数据拥有原始数据属性值的一份精确拷贝。如果属性是引用类型,拷贝的就是内存地址(深层次的引用类型共享内存地址)。

实现一个浅拷贝:

function shallowClone(obj) {
  const newObj = {};
  for (const prop in obj) {
    if (obj.hasOwnProperty(prop)) {
      newObj[prop] = obj[prop];
    }
  }
  return newObj;
}

典型的浅拷贝方式:

  • Object.assign({}, obj)
  • Array.prototype.concat / Array.prototype.slice
  • 扩展运算符 { ...obj } / [...arr]

2. 深拷贝以及方式

深拷贝开辟一个新栈,属性完全相同,但对应两个不同的地址,修改一个对象的属性不会改变另一个。

常见深拷贝方式

(1) _.cloneDeep()(lodash)

const _ = require('lodash');
const obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] };
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f); // false

(2) $.extend()(jQuery)

const $ = require('jquery');
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false

(3) JSON.parse(JSON.stringify())

const obj2 = JSON.parse(JSON.stringify(obj1));

弊端:会忽略 undefined、Symbol 和函数,不能处理循环引用、Date / RegExp / Map / Set 等。

const obj = { name: 'A', name1: undefined, name3: function () {}, name4: Symbol('A') };
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // { name: 'A' }

(4) 循环递归(最完整实现)

function deepClone(obj, hash = new WeakMap()) {
  // 解决循环引用导致的爆栈
  if (obj === null) return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (typeof obj !== 'object') return obj; // 函数不深拷贝
  if (hash.get(obj)) return hash.get(obj);

  const cloneObj = new obj.constructor();
  hash.set(obj, cloneObj);

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}

补充:

  • 现代浏览器可直接使用 structuredClone()(原生 API)实现深拷贝,支持 Date、Map、Set、ArrayBuffer、循环引用,但不能克隆函数、原型链。
  • 处理循环引用必须借助 WeakMap / Map 记录已拷贝对象。
  • 函数、Symbol 属性、原型链是否要拷贝需要根据业务决定。

来源整理自:我的有道云笔记