js深拷贝与浅拷贝

37 阅读2分钟

浅拷贝 (Shallow Copy)

浅拷贝只复制对象的第一层属性,如果属性是引用类型,则复制的是引用(内存地址),而不是实际的值。

浅拷贝的实现方式

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

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

    const original = { a: 1, b: { c: 2 } };
    const shallowCopy = Object.assign({}, original);
    
  3. 数组的浅拷贝方法

    const originalArray = [1, 2, { a: 3 }];
    const shallowCopyArray = originalArray.slice();
    // 或
    const shallowCopyArray2 = [...originalArray];
    // 或
    const shallowCopyArray3 = originalArray.concat();
    

浅拷贝的问题

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

original.a = 10; // 不会影响浅拷贝
original.b.c = 20; // 会影响浅拷贝,因为b是引用类型

console.log(shallowCopy.a); // 1 (未受影响)
console.log(shallowCopy.b.c); // 20 (受影响)

深拷贝 (Deep Copy)

深拷贝会复制对象的所有层级,创建一个完全独立的新对象,包括所有嵌套的对象和数组。

深拷贝的实现方式

  1. JSON.parse(JSON.stringify())

    const original = { a: 1, b: { c: 2 } };
    const deepCopy = JSON.parse(JSON.stringify(original));
    
    • 缺点:无法处理函数、Symbol、undefined、循环引用等
  2. 使用第三方库

    • Lodash 的 _.cloneDeep()

      const _ = require('lodash');
      const original = { a: 1, b: { c: 2 } };
      const deepCopy = _.cloneDeep(original);
      
  3. 手动实现深拷贝

    function deepClone(obj, hash = new WeakMap()) {
      if (obj === null || typeof obj !== 'object') return obj;
      if (hash.has(obj)) return hash.get(obj);
      
      const result = Array.isArray(obj) ? [] : {};
      hash.set(obj, result);
      
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          result[key] = deepClone(obj[key], hash);
        }
      }
      
      return result;
    }
    
  4. 现代浏览器 API

    • structuredClone()

      const original = { a: 1, b: { c: 2 } };
      const deepCopy = structuredClone(original);
      

深拷贝的注意事项

const obj = {
  a: 1,
  b: { c: 2 },
  d: new Date(),
  e: function() { console.log('e'); },
  f: undefined,
  g: Symbol('sym'),
  h: new RegExp(/abc/, 'g')
};

// JSON方法会丢失函数、undefined、Symbol等
const jsonCopy = JSON.parse(JSON.stringify(obj));
console.log(jsonCopy); // { a: 1, b: { c: 2 }, d: "ISO日期字符串", h: {} }

// structuredClone可以处理更多类型但不能处理函数
const structuredCopy = structuredClone(obj); // 会报错,因为不能克隆函数

如何选择

  • 如果对象没有嵌套或嵌套的都是基本类型,使用浅拷贝
  • 如果需要完全独立的副本,使用深拷贝
  • 对于复杂对象的深拷贝,推荐使用 structuredClone() 或 Lodash 的 _.cloneDeep()

循环引用问题

const obj = { a: 1 };
obj.self = obj;

// JSON方法会报错
// JSON.parse(JSON.stringify(obj)); // TypeError: Converting circular structure to JSON

// 使用带有WeakMap的手动实现可以解决
const cloned = deepClone(obj); // 使用上面的手动实现
console.log(cloned.self === cloned); // true

深拷贝和浅拷贝是JavaScript中处理对象复制的重要概念,理解它们的区别和适用场景对于编写健壮的代码非常重要。