返回

JavaScript 深浅拷贝你真的会用吗?

前端

在 JavaScript 中,拷贝对象或数组时,我们可以选择深拷贝或浅拷贝。那么,深拷贝和浅拷贝有什么区别?又该如何选择合适的拷贝方式呢?

深拷贝与浅拷贝

浅拷贝 是指只拷贝对象的引用,而不是拷贝对象本身。这意味着,如果我们对浅拷贝的对象进行修改,那么原始对象也会受到影响。

深拷贝 是指不仅拷贝对象的引用,还会拷贝对象本身。这意味着,如果我们对深拷贝的对象进行修改,那么原始对象不会受到影响。

浅拷贝的实现方法

JavaScript 中实现浅拷贝的方法有很多,最常见的方法是使用 Object.assign() 方法。

const obj1 = { name: 'John', age: 30 };
const obj2 = Object.assign({}, obj1);

obj2.name = 'Mary';

console.log(obj1); // { name: 'John', age: 30 }
console.log(obj2); // { name: 'Mary', age: 30 }

深拷贝的实现方法

实现深拷贝的方法有很多,最常见的方法是使用递归或循环。

递归

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

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

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

  return newObj;
}

循环

function deepCopy(obj) {
  const queue = [{ obj, parent: null, key: null }];
  const visited = new WeakMap();
  const result = {};

  while (queue.length > 0) {
    const { obj, parent, key } = queue.shift();

    if (visited.has(obj)) {
      parent[key] = visited.get(obj);
      continue;
    }

    visited.set(obj, obj);

    if (Array.isArray(obj)) {
      parent[key] = [];
      obj.forEach((item, index) => {
        queue.push({ obj: item, parent: parent[key], key: index });
      });
      continue;
    }

    if (typeof obj === 'object' && obj !== null) {
      parent[key] = {};
      for (const key in obj) {
        queue.push({ obj: obj[key], parent: parent[key], key });
      }
      continue;
    }

    parent[key] = obj;
  }

  return result;
}

何时使用深拷贝,何时使用浅拷贝?

浅拷贝 适用于以下场景:

  • 当我们需要拷贝一个对象或数组,并且不希望修改原始对象或数组时。
  • 当我们需要拷贝一个对象或数组,并且知道对象或数组中不包含任何引用类型的数据时。

深拷贝 适用于以下场景:

  • 当我们需要拷贝一个对象或数组,并且希望修改拷贝后的对象或数组时。
  • 当我们需要拷贝一个对象或数组,并且知道对象或数组中包含引用类型的数据时。

总结

深拷贝和浅拷贝是 JavaScript 中常用的两种拷贝方式。了解不同实现方法和实现粒度的优劣及其适合的场景,可以帮助我们选择最合适的拷贝方式,避免潜在的陷阱。