对象拷贝的浅复制与深复制实现

在JavaScript开发中,对象拷贝是一个常见但容易出错的操作。理解浅复制(shallow copy)和深复制(deep copy)的区别及实现方式,对于编写健壮的JavaScript代码至关重要。

浅复制(Shallow Copy)

浅复制只复制对象的第一层属性,如果属性值是引用类型(如对象、数组等),则复制的是引用而非实际对象本身。

浅复制的实现方式

  1. 展开运算符(...)
javascript 复制代码
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
  1. Object.assign()
javascript 复制代码
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
  1. 数组的浅复制
javascript 复制代码
const originalArray = [1, 2, { a: 3 }];
const shallowCopyArray = [...originalArray];
// 或
const shallowCopyArray2 = originalArray.slice();

深复制(Deep Copy)

深复制会递归复制对象的所有层级,创建一个完全独立的副本。

深复制的实现方式

  1. JSON方法(有局限性)
javascript 复制代码
const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));

注意:此方法无法复制函数、Symbol、undefined等特殊类型

  1. 递归实现
javascript 复制代码
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  
  if (obj instanceof Array) {
    const copy = [];
    for (let i = 0; i < obj.length; i++) {
      copy[i] = deepClone(obj[i]);
    }
    return copy;
  }
  
  if (obj instanceof Object) {
    const copy = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        copy[key] = deepClone(obj[key]);
      }
    }
    return copy;
  }
  
  throw new Error("Unable to copy object");
}
  1. 使用第三方库
javascript 复制代码
// 使用lodash
const _ = require('lodash');
const deepCopy = _.cloneDeep(original);

性能与选择考虑

  • 浅复制:速度快,内存占用少,适合简单对象或确定嵌套结构不会改变的情况
  • 深复制:更安全但性能开销大,适合复杂对象或不确定嵌套结构是否会改变的情况

原型链与拷贝

当拷贝对象涉及原型链时,需要注意:

javascript 复制代码
function Person(name) {
  this.name = name;
}
Person.prototype.greet = function() {
  console.log(`Hello, ${this.name}`);
};

const john = new Person('John');
const shallowCopy = { ...john }; // 会丢失原型方法
const deepCopy = deepClone(john); // 也会丢失原型方法

// 保持原型链的拷贝
const protoCopy = Object.create(
  Object.getPrototypeOf(john),
  Object.getOwnPropertyDescriptors(john)
);

在实际开发中,应根据具体需求选择合适的拷贝方式,理解每种方式的优缺点,才能写出更健壮的代码。