深拷贝与浅拷贝

JavaScript 中深拷贝与浅拷贝的区别及实现方式

问题

深拷贝与浅拷贝的区别是什么?如何实现深拷贝?

解答

浅拷贝

浅拷贝只复制对象的第一层属性,嵌套对象仍然是引用关系。

// 浅拷贝方法
const obj = { a: 1, b: { c: 2 } };

// 方法1: Object.assign
const copy1 = Object.assign({}, obj);

// 方法2: 展开运算符
const copy2 = { ...obj };

// 验证:修改嵌套对象会影响原对象
copy1.b.c = 100;
console.log(obj.b.c); // 100 - 原对象被修改了

深拷贝

深拷贝会递归复制所有层级,新对象与原对象完全独立。

// 方法1: JSON 序列化(简单但有局限)
const deepCopy1 = JSON.parse(JSON.stringify(obj));

// 方法2: 手写递归实现
function deepClone(obj, map = new WeakMap()) {
  // 处理基本类型和 null
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 处理循环引用
  if (map.has(obj)) {
    return map.get(obj);
  }

  // 处理 Date
  if (obj instanceof Date) {
    return new Date(obj);
  }

  // 处理 RegExp
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

  // 处理数组和普通对象
  const clone = Array.isArray(obj) ? [] : {};
  map.set(obj, clone);

  // 递归复制所有属性
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], map);
    }
  }

  return clone;
}

// 测试
const original = {
  name: 'test',
  nested: { value: 1 },
  arr: [1, 2, { x: 3 }],
  date: new Date(),
};
original.self = original; // 循环引用

const cloned = deepClone(original);
cloned.nested.value = 999;

console.log(original.nested.value); // 1 - 原对象不受影响
console.log(cloned.self === cloned); // true - 循环引用正确处理

JSON 方法的局限性

const obj = {
  fn: function() {},    // 函数会丢失
  undef: undefined,     // undefined 会丢失
  sym: Symbol('test'),  // Symbol 会丢失
  date: new Date(),     // 变成字符串
  reg: /test/g,         // 变成空对象
};

const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);
// { date: "2024-01-01T00:00:00.000Z", reg: {} }

关键点

  • 浅拷贝只复制第一层,嵌套对象共享引用
  • 深拷贝递归复制所有层级,完全独立
  • JSON.parse(JSON.stringify()) 简单但无法处理函数、undefined、Symbol、循环引用
  • 手写深拷贝需要用 WeakMap 处理循环引用
  • 特殊对象(Date、RegExp、Map、Set)需要单独处理