返回

深拷贝与浅拷贝:对象复制的奥秘

前端



掌握深浅拷贝,轻松实现对象复制


在编程的世界里,数据复制是再常见不过的操作了。在JavaScript中,对象复制主要有两种方式:深拷贝和浅拷贝。本文将深入探讨深浅拷贝的含义、区别和实现方式,帮助你更轻松地驾驭对象复制。


一、深拷贝和浅拷贝

首先,我们要明确一点,JavaScript中的数据类型分为:

  • 基本数据类型(Number, String, Boolean, Null, Undefined, Symbol)
  • 对象数据类型(Object, Array, Function)

当复制基本数据类型时,会创建该值的全新副本,而复制对象数据类型时,则会创建指向该对象的引用。

1. 浅拷贝

浅拷贝只复制对象的第一层属性,不会复制嵌套对象或数组。也就是说,浅拷贝只复制对象本身的属性值,而不会复制对象内部包含的其他对象或数组。

const obj1 = {
  name: 'John',
  age: 30,
  address: {
    street: '123 Main Street',
    city: 'New York',
    state: 'NY',
  },
};

const obj2 = {...obj1};

// 修改obj2的属性值
obj2.age = 35;
obj2.address.street = '456 Elm Street';

// 打印obj1和obj2
console.log(obj1);
// {
//   name: 'John',
//   age: 30,
//   address: {
//     street: '456 Elm Street',
//     city: 'New York',
//     state: 'NY',
//   },
// }
console.log(obj2);
// {
//   name: 'John',
//   age: 35,
//   address: {
//     street: '456 Elm Street',
//     city: 'New York',
//     state: 'NY',
//   },
// }

从上面的例子可以看出,当修改obj2的属性值时,obj1的属性值也会发生改变。这是因为浅拷贝只复制了obj1的引用,而不是创建obj1的全新副本。因此,修改obj2的属性值实际上就是修改了obj1的属性值。

2. 深拷贝

深拷贝不仅复制对象的第一层属性,还会复制嵌套对象或数组。也就是说,深拷贝会创建对象及其内部包含的所有对象或数组的全新副本。

实现深拷贝的方法有很多,最常用的方法是使用递归。递归是一种函数调用自身的方法,可以通过不断递归调用将对象及其内部的所有对象或数组都复制一遍。

function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  if (Array.isArray(obj)) {
    return obj.map(item => deepCopy(item));
  }

  const newObj = {};
  for (const key in obj) {
    newObj[key] = deepCopy(obj[key]);
  }

  return newObj;
}

const obj1 = {
  name: 'John',
  age: 30,
  address: {
    street: '123 Main Street',
    city: 'New York',
    state: 'NY',
  },
};

const obj2 = deepCopy(obj1);

// 修改obj2的属性值
obj2.age = 35;
obj2.address.street = '456 Elm Street';

// 打印obj1和obj2
console.log(obj1);
// {
//   name: 'John',
//   age: 30,
//   address: {
//     street: '123 Main Street',
//     city: 'New York',
//     state: 'NY',
//   },
// }
console.log(obj2);
// {
//   name: 'John',
//   age: 35,
//   address: {
//     street: '456 Elm Street',
//     city: 'New York',
//     state: 'NY',
//   },
// }

从上面的例子可以看出,当修改obj2的属性值时,obj1的属性值不会发生改变。这是因为深拷贝创建了obj1的全新副本,而不是复制obj1的引用。因此,修改obj2的属性值不会影响obj1。

二、何时使用深拷贝和浅拷贝

浅拷贝和深拷贝各有优缺点,在不同的场景下使用不同的拷贝方式可以达到更好的效果。

1. 浅拷贝的优点和缺点

优点:

  • 速度快,因为只需要复制对象本身的属性值,而不需要复制嵌套对象或数组。
  • 内存占用少,因为只创建了一个对象副本,而不需要创建嵌套对象或数组的副本。

缺点:

  • 无法复制嵌套对象或数组,如果对象内部包含嵌套对象或数组,浅拷贝只能复制对象本身的属性值,而无法复制嵌套对象或数组。
  • 容易出现数据浅层复制陷阱,如果修改对象副本的属性值,可能会影响到原始对象。

2. 深拷贝的优点和缺点

优点:

  • 可以复制嵌套对象或数组,深拷贝会创建对象及其内部的所有对象或数组的全新副本。
  • 避免数据浅层复制陷阱,修改对象副本的属性值不会影响到原始对象。

缺点:

  • 速度慢,因为需要复制对象及其内部的所有对象或数组。
  • 内存占用多,因为需要创建对象及其内部的所有对象或数组的副本。

三、总结

深拷贝和浅拷贝都是JavaScript中常用的对象复制方式,各有优缺点,在不同的场景下使用不同的拷贝方式可以达到更好的效果。浅拷贝速度快、内存占用少,但无法复制嵌套对象或数组,容易出现数据浅层复制陷阱。深拷贝可以复制嵌套对象或数组,避免数据浅层复制陷阱,但速度慢、内存占用多。在实际开发中,需要根据具体情况选择合适的拷贝方式。