深拷贝(Deep Copy)

167 阅读3分钟

深拷贝(Deep Copy)是指在复制对象时不仅复制对象本身,还复制对象内部的所有引用对象。这样可以确保新对象与原对象完全独立,修改其中一个对象不会影响另一个对象。

深拷贝的重要性

  1. 避免副作用:修改一个对象时不会影响另一个对象。
  2. 保证数据独立性:确保新对象与原对象之间没有引用关系。

深拷贝的方法

在JavaScript中,有多种方法可以实现深拷贝,包括:

  1. JSON 方法
  2. 递归方法
  3. 使用库函数(如 Lodash)

1. JSON 方法

使用 JSON.parse() 和 JSON.stringify() 来实现深拷贝。

示例代码

javascript
function deepCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

// 示例对象
const obj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4, 5]
  }
};

// 深拷贝
const newObj = deepCopy(obj);

// 修改原对象
obj.a = 10;
obj.b.c = 20;

console.log(obj); // { a: 10, b: { c: 20, d: [3, 4, 5] } }
console.log(newObj); // { a: 1, b: { c: 2, d: [3, 4, 5] } }

2. 递归方法

使用递归函数来实现深拷贝。

示例代码

javascript
function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

function deepCopy(obj) {
  let copy;

  // 处理 Date 和 RegExp 类型
  if (obj instanceof Date) {
    copy = new Date(obj);
  } else if (obj instanceof RegExp) {
    copy = new RegExp(obj);
  } else {
    // 处理普通对象和数组
    copy = isObject(obj) ? Object.create(Object.getPrototypeOf(obj)) : Array.isArray(obj) ? [] : {};
  }

  for (let key in obj) {
    if (isObject(obj[key])) {
      copy[key] = deepCopy(obj[key]);
    } else {
      copy[key] = obj[key];
    }
  }

  return copy;
}

// 示例对象
const obj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4, 5],
    e: new Date(),
    f: /test/
  }
};

// 深拷贝
const newObj = deepCopy(obj);

// 修改原对象
obj.a = 10;
obj.b.c = 20;

console.log(obj); // { a: 10, b: { c: 20, d: [3, 4, 5], e: [Date], f: /test/ } }
console.log(newObj); // { a: 1, b: { c: 2, d: [3, 4, 5], e: [Date], f: /test/ } }

3. 使用库函数(如 Lodash)

使用 Lodash 库中的 _.cloneDeep() 函数来实现深拷贝。

示例代码

javascript
// 引入 Lodash
const _ = require('lodash');

// 示例对象
const obj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4, 5],
    e: new Date(),
    f: /test/
  }
};

// 深拷贝
const newObj = _.cloneDeep(obj);

// 修改原对象
obj.a = 10;
obj.b.c = 20;

console.log(obj); // { a: 10, b: { c: 20, d: [3, 4, 5], e: [Date], f: /test/ } }
console.log(newObj); // { a: 1, b: { c: 2, d: [3, 4, 5], e: [Date], f: /test/ } }

深拷贝的注意事项

  1. 循环引用:如果对象中有循环引用,上述方法可能会导致无限递归。需要特殊处理。
  2. 原型链:深拷贝不会复制对象的原型链,只会复制对象自身的属性。
  3. 函数和正则表达式:深拷贝不会复制函数和正则表达式的引用,而是复制新的实例。

示例:处理循环引用

如果对象中有循环引用,需要特殊处理以避免无限递归。

示例代码

javascript
function deepCopy(obj, hash = new WeakMap()) {
  if (!isObject(obj)) return obj;
  if (hash.has(obj)) return hash.get(obj);

  let copy = Array.isArray(obj) ? [] : {};
  hash.set(obj, copy);

  for (let key in obj) {
    if (isObject(obj[key])) {
      copy[key] = deepCopy(obj[key], hash);
    } else {
      copy[key] = obj[key];
    }
  }

  return copy;
}

// 示例对象
const obj = {
  a: 1,
  b: {
    c: 2,
    d: [3, 4, 5]
  }
};
obj.b.e = obj; // 循环引用

// 深拷贝
const newObj = deepCopy(obj);

// 修改原对象
obj.a = 10;
obj.b.c = 20;

console.log(obj); // { a: 10, b: { c: 20, d: [3, 4, 5], e: [Circular] } }
console.log(newObj); // { a: 1, b: { c: 2, d: [3, 4, 5], e: {} } }

总结

深拷贝在JavaScript中非常重要,可以确保新对象与原对象完全独立。以下是几种常用的深拷贝方法:

  1. JSON 方法:简单易用,但不支持循环引用和某些特殊类型。
  2. 递归方法:灵活且支持循环引用。
  3. 使用库函数(如 Lodash) :方便快捷,但需要引入额外依赖。

希望这些示例和说明对你有所帮助!如果有更多问题或需要进一步的帮助,请随时提问。