目录

理解-JavaScript-中的浅拷贝与深拷贝

理解 JavaScript 中的浅拷贝与深拷贝

在 JavaScript 开发中,我们经常需要复制对象或数组。然而,复制的方式不同,可能会导致不同的结果。本文将详细介绍 浅拷贝深拷贝 的概念、区别以及实现方式,帮助你更好地理解和使用它们。

1. 什么是浅拷贝?

定义

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

特点

  • 新对象和原对象共享内部的引用类型字段。
  • 修改原对象的引用类型字段,浅拷贝的对象也会受到影响。

https://i-blog.csdnimg.cn/direct/429f84b4a7d8427da1068928b7318e55.png

实现方式

在 JavaScript 中,浅拷贝可以通过以下方式实现:

  • 使用 Object.assign()
  • 使用扩展运算符 {...obj}
  • 使用数组的 slice()concat() 方法(针对数组)

示例

const original = { a: 1, b: { c: 2 } };
const shallowCopied = { ...original }; // 使用扩展运算符实现浅拷贝

// 修改原对象的引用类型字段
original.b.c = 100;

console.log(original);      // { a: 1, b: { c: 100 } }
console.log(shallowCopied); // { a: 1, b: { c: 100 } },浅拷贝对象也被修改

2. 什么是深拷贝?

定义

深拷贝是指递归地复制对象及其内部的所有引用类型字段,生成一个完全独立的新对象。

特点

  • 新对象和原对象完全独立。
  • 修改原对象的引用类型字段,深拷贝的对象不会受到影响。

https://i-blog.csdnimg.cn/direct/41e265fd546f48d499add6b234f8ea4b.png

实现方式

深拷贝的实现方式有多种,以下是常见的几种方法:

方法 1:使用 JSON.parse(JSON.stringify(obj))
  • 优点 :简单易用。

  • 缺点

    • 不能处理函数、 undefinedSymbol 等特殊类型。
    • 不能处理循环引用(即对象内部属性引用自身)。
const original = { a: 1, b: { c: 2 } };
const deepCopied = JSON.parse(JSON.stringify(original));

// 修改原对象的引用类型字段
original.b.c = 100;

console.log(original);    // { a: 1, b: { c: 100 } }
console.log(deepCopied);  // { a: 1, b: { c: 2 } },深拷贝对象不受影响
方法 2:递归实现深拷贝
  • 优点 :可以处理更多数据类型(如函数、 undefined 等)。
  • 缺点 :需要手动处理循环引用。
function deepClone(obj, cache = new WeakMap()) {
  // 如果是基本类型或 null,直接返回
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 如果对象已经被拷贝过,直接返回缓存的结果(避免循环引用)
  if (cache.has(obj)) {
    return cache.get(obj);
  }

  // 处理数组
  if (Array.isArray(obj)) {
    const clonedArr = [];
    cache.set(obj, clonedArr);
    obj.forEach((item) => {
      clonedArr.push(deepClone(item, cache));
    });
    return clonedArr;
  }

  // 处理普通对象
  const clonedObj = {};
  cache.set(obj, clonedObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clonedObj[key] = deepClone(obj[key], cache);
    }
  }

  return clonedObj;
}

// 测试
const original = { a: 1, b: { c: 2 }, d: [3, 4] };
const deepCopied = deepClone(original);

// 修改原对象
original.b.c = 100;
original.d.push(5);

console.log(original);    // { a: 1, b: { c: 100 }, d: [3, 4, 5] }
console.log(deepCopied);  // { a: 1, b: { c: 2 }, d: [3, 4] },深拷贝对象不受影响
方法 3:使用第三方库(如 Lodash)
  • 优点 :功能强大,支持各种数据类型和循环引用。
  • 缺点 :需要引入额外的库。
// 安装 lodash:npm install lodash
const _ = require('lodash');

const original = { a: 1, b: { c: 2 } };
const deepCopied = _.cloneDeep(original);

// 修改原对象
original.b.c = 100;

console.log(original);    // { a: 1, b: { c: 100 } }
console.log(deepCopied);  // { a: 1, b: { c: 2 } },深拷贝对象不受影响

3. 浅拷贝 vs 深拷贝

特性浅拷贝深拷贝
复制层级只复制第一层属性递归复制所有层级属性
引用类型字段共享引用地址完全独立
修改原对象影响影响浅拷贝对象不影响深拷贝对象
实现方式Object.assign() 、扩展运算符等JSON.parse(JSON.stringify()) 、递归、Lodash 等

4. 如何选择拷贝方式?

  • 浅拷贝 :适用于对象结构简单或不需要独立内部引用的情况。
  • 深拷贝 :适用于对象结构复杂或需要完全独立副本的情况。

5. 总结

在 JavaScript 中,浅拷贝和深拷贝是两种常见的对象复制方式。浅拷贝只复制对象的第一层属性,而深拷贝会递归复制对象及其所有内部引用类型字段。根据实际需求选择合适的拷贝方式,可以避免不必要的 bug 和数据污染。

希望本文能帮助你更好地理解浅拷贝和深拷贝的概念与实现方式。如果你有更多问题,欢迎在评论区留言讨论!