JavaScript 中的浅复制与深复制

概述

在 JavaScript 中,复制对象或数组可分为浅复制和深复制。处理复杂数据结构(尤其是包含嵌套对象或数组的数据结构)时,了解两者的区别至关重要。

本指南解释了这些概念、它们的特征、实现方法以及何时使用它们。

1.浅拷贝

**定义**

浅拷贝会创建对象或数组顶层属性的副本。但是,对于嵌套对象或数组,只会复制引用,而不会复制实际数据。

**特征**

  • 仅复制第一级的属性或元素。
  • 嵌套对象或数组与原始对象/数组共享引用。
  • 对于简单结构来说轻量且高效,但不适合嵌套数据的独立复制。
  • **1.1 使用 Object.assign()**

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, original);
    
    shallowCopy.b.c = 42; 
    console.log(original.b.c); // OUTPUT: 42 (The original object also affected due to shared reference)

    **1.2 使用展开运算符 (...)**

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = { ...original };
    
    shallowCopy.b.c = 90;
    console.log(original.b.c); // OUTPUT: 90

    **1.3 让我们看一个数组方法(slice、concat)浅拷贝的例子**

    const original = [1, 2, [3, 4]];
    const shallowCopy = original.slice();
    
    shallowCopy[2][0] = 10; 
    console.log(original[2][0]); // OUTPUT: 10

    2.深层复制

    **定义**

    深层复制会创建对象或数组的完全独立副本。所有层级(包括嵌套结构)均会递归复制。对复制结构的更改不会影响原始结构,反之亦然。

    **特征**

  • 递归复制结构的所有级别。
  • 嵌套对象或数组与原始对象或数组无关。
  • 计算量比浅拷贝大。
  • **2.1 使用 JSON.stringify() 和 JSON.parse()**

  • 将对象转换为 JSON 字符串,然后转换回新对象。
  • 限制:不处理函数、日期、正则表达式或循环引用。
  • const original = { a: 1, b: { c: 2 } };
    const deepCopy = JSON.parse(JSON.stringify(original));
    
    deepCopy.b.c = 42;
    console.log(original.b.c); // OUTPUT: 2 (original remains unaffected)

    **2.2 使用structuredClone()**

    一种支持循环引用和特殊对象(如日期)的深度复制的现代方法。

    const original = { a: 1, b: { c: 2 }, date: new Date() };
    const deepCopy = structuredClone(original);
    
    deepCopy.b.c = 42;
    console.log(original.b.c); // OUTPUT: 2
    console.log(original.date === deepCopy.date); // FALSE

    **2.3 使用自定义递归函数**

    一种手动处理复杂案例的灵活解决方案。

    function deepCopy(obj) {
      if (obj === null || typeof obj !== "object") return obj; 
    
      if (Array.isArray(obj)) return obj.map(deepCopy); 
    
      const copy = {};
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          copy[key] = deepCopy(obj[key]);
        }
      }
      return copy;
    }
    
    const original = { a: 1, b: { c: 2 } };
    const deepCopyObj = deepCopy(original);
    
    deepCopyObj.b.c = 42;
    console.log(original.b.c); // OUTPUT: 2

    3. 何时使用?

    **浅拷贝**

  • 适用于平面对象或没有嵌套结构的数组。
  • 当性能至关重要时,嵌套对象的共享引用是可以接受的。
  • **深层复制**

  • 对于复杂、深度嵌套的对象/数组。
  • 当您需要副本和原件之间真正的独立性时。
  • 4. 总结

    **浅拷贝**

  • 簡單、高效。
  • 适用于扁平结构或可以接受共享参考的情况。
  • **深层复制**

  • 坚固并确保完全独立。
  • 非常适合深度嵌套或复杂的结构。
  • 这是对 JavaScript 中对象的浅拷贝和深拷贝的深入解释。请根据您的用例和性能要求选择适当的方法。