一、浅拷贝与深拷贝的概念

浅拷贝 (Shallow Copy)

  • 只复制对象的第一层属性
  • 如果属性是基本类型,拷贝其值
  • 如果属性是引用类型,拷贝其内存地址(即共享同一引用)
  • 修改拷贝后的对象的引用类型属性会影响原对象

深拷贝 (Deep Copy)

  • 递归复制对象的所有层级
  • 无论属性是基本类型还是引用类型,都重新创建
  • 拷贝后的对象与原对象完全独立,互不影响

二、浅拷贝的实现方法

1. Object.assign()

const obj = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, obj);

2. 展开运算符(...)

const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj };

3. Array.prototype.slice() (数组)

const arr = [1, 2, [3, 4]];
const shallowCopy = arr.slice();

4. Array.prototype.concat() (数组)

const arr = [1, 2, [3, 4]];
const shallowCopy = [].concat(arr);

三、深拷贝的实现方法

1. JSON.parse(JSON.stringify()) - 最简单但有局限

function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

局限性

  • 不能处理函数、undefined、Symbol
  • 不能处理循环引用
  • 会丢失对象的constructor,所有构造函数都会指向Object
  • 日期对象会变成字符串

2. 递归实现 - 基础版

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  const clone = Array.isArray(obj) ? [] : {};
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key]);
    }
  }
  
  return clone;
}

3. 递归实现 - 完整版(处理更多类型)

function deepClone(obj, hash = new WeakMap()) {
  // 处理基本类型和null/undefined
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  // 处理日期
  if (obj instanceof Date) {
    return new Date(obj);
  }
  
  // 处理正则
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }
  
  // 处理循环引用
  if (hash.has(obj)) {
    return hash.get(obj);
  }
  
  // 处理数组和对象
  const clone = Array.isArray(obj) ? [] : {};
  hash.set(obj, clone);
  
  // 处理Symbol属性
  const symKeys = Object.getOwnPropertySymbols(obj);
  if (symKeys.length > 0) {
    symKeys.forEach(symKey => {
      clone[symKey] = deepClone(obj[symKey], hash);
    });
  }
  
  // 处理普通属性
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], hash);
    }
  }
  
  return clone;
}

4. 使用第三方库

  • lodash 的 _.cloneDeep()
const _ = require('lodash');
const deepCopy = _.cloneDeep(originalObj);
  • jQuery 的 $.extend(true, {}, obj)
const deepCopy = $.extend(true, {}, originalObj);

四、深拷贝的注意事项

  1. 循环引用问题:对象内部有循环引用时,简单递归会导致栈溢出,需要使用WeakMap记录已拷贝对象
  2. 特殊对象处理:需要特别处理Date、RegExp、Set、Map等内置对象
  3. 函数拷贝:函数是否需要拷贝取决于具体需求,通常函数是共享的
  4. 原型链:是否需要保持原型链关系
  5. 性能考虑:深拷贝是昂贵的操作,特别是对于大型对象

五、实际应用建议

  1. 对于简单对象且不包含特殊类型,可以使用JSON.parse(JSON.stringify())
  2. 对于复杂对象,建议使用lodash的_.cloneDeep()
  3. 如果性能是关键考虑因素,可以考虑只拷贝需要的部分而非整个对象

六、性能比较

通常性能排序(从快到慢):

  1. 浅拷贝
  2. JSON方法(但有局限性)
  3. 完整的递归深拷贝
  4. 第三方库(功能最全但稍慢)