【JavaScript】浅拷贝和深拷贝

2023-12-28 19:50:01

JavaScript中的对象拷贝是我们在日常开发中经常遇到的一个问题。深浅拷贝是两种常见的拷贝方式,它们分别适用于不同的场景。本文将深入探讨JavaScript中浅拷贝和深拷贝的概念、区别以及实现方式。

浅拷贝

浅拷贝是指将一个对象的属性值复制到另一个对象,如果属性值是基本数据类型(如数字、字符串、布尔等),则直接复制该值;如果属性值是引用数据类型(如对象、数组等),则复制的是其引用地址,而不是实际的对象本身。

浅拷贝的实现方式

  • Object.assign()
let source = { name: '李华', age: 30 };
let shallowCopy = Object.assign({}, source);
  • 扩展运算符(…)
let source = { name: '李华', age: 30 };
let shallowCopy = { ...source };
  • Array.prototype.concat()(用于数组的浅拷贝)
let array = [1, 2, 3];
let shallowCopy = array.concat();

深拷贝

深拷贝是指将一个对象从堆内存中完全复制一份到栈内存中,新对象与原对象是完全独立的,修改新对象不会影响原对象。

深拷贝的实现方式

  • 递归实现:
function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  let result = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      result[key] = deepCopy(obj[key]);
    }
  }

  return result;
}
  • JSON.stringify()和JSON.parse():
let source = { name: '李华', age: 30 };
let deepCopy = JSON.parse(JSON.stringify(source));
  • Lodash
    lodash中的 cloneDeep()函数用于创建原始对象的完全独立副本,确保修改拷贝对象的属性不会影响原始对象。
// 引入Lodash
const _ = require('lodash');

// 原始对象
const originalObject = {
  name: '李华',
  age: 30,
  nestedObject: {
    sex: '男',
    country: '中国'
  }
};

// 使用Lodash的深拷贝函数 _.cloneDeep()
const deepCopyObject = _.cloneDeep(originalObject);

// 修改拷贝对象的某些属性
deepCopyObject.name = '李明';
deepCopyObject.nestedObject.sex= '女';

// 打印原始对象和拷贝对象,观察是否相互影响
console.log('原始对象:', originalObject);
console.log('深拷贝对象:', deepCopyObject);

  • jQuery实现
    jQuery虽然没有提供专门的深拷贝函数,但我们可以使用jQuery.extend()函数来实现浅拷贝,结合递归来实现深拷贝。
// 原始对象
const originalObject = {
  name: '李华',
  age: 30,
  nestedObject: {
    sex: '男',
    country: '中国'
  }
};

// 使用 jQuery.extend() 进行深拷贝
var deepCopyObject = jQuery.extend(true, {}, originalObject);

// 修改拷贝对象的某些属性
deepCopyObject.name = '李明';
deepCopyObject.nestedObject.sex= '女';

// 打印原始对象和拷贝对象,观察是否相互影响
console.log('原始对象:', originalObject);
console.log('深拷贝对象:', deepCopyObject);

  • Immer
    Immer 是一个用于创建不可变数据结构的 JS 库。它提供了一种简单且直观的方式来修改不可变数据,而不必手动编写深拷贝代码。Immer 的 produce 函数可以自动处理深拷贝。
// 引入 Immer
import produce from 'immer';

// 原始对象
const originalObject = {
  name: '李华',
  age: 30,
  nestedObject: {
    sex: '男',
    country: '中国'
  }
};

// 使用 Immer 的 produce 函数进行深拷贝
const deepCopyObject = produce(originalObject, draft => {
  // 修改 draft 对象的某些属性
  draft.name = '李明';
  draft.nestedObject.sex= '女';
});

// 打印原始对象和拷贝对象,观察是否相互影响
console.log('原始对象:', originalObject);
console.log('深拷贝对象:', deepCopyObject);

  • 纯JS实现
    如果不想使用第三方库实现深拷贝,可以通过JS简单的递归来实现。
function deepCopy(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj; // 如果是基本数据类型或 null,直接返回
  }

  // 根据原始对象的类型创建一个新的对象或数组
  const copy = Array.isArray(obj) ? [] : {};

  // 遍历原始对象的属性
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 递归深拷贝嵌套对象或数组
      copy[key] = deepCopy(obj[key]);
    }
  }

  return copy;
}

// 原始对象
const originalObject = {
  name: '李华',
  age: 30,
  nestedObject: {
    sex: '男',
    country: '中国'
  }
};

// 深拷贝对象
const deepCopyObject = deepCopy(originalObject);

// 修改拷贝对象的某些属性
deepCopyObject.name = '李明';
deepCopyObject.nestedObject.sex= '女';

// 打印原始对象和拷贝对象,观察是否相互影响
console.log('原始对象:', originalObject);
console.log('深拷贝对象:', deepCopyObject);

浅拷贝与深拷贝的区别

1.对于基本数据类型:

  • 浅拷贝:复制基本数据类型的值。
  • 深拷贝:创建一个新的基本数据类型值。

2.对于引用数据类型:

  • 浅拷贝:复制引用,指向相同的对象。
  • 深拷贝:复制引用指向的对象,生成一个新的对象。

使用场景

  • 使用浅拷贝当你只需要复制对象的第一层属性,而不需要考虑嵌套对象。
  • 使用深拷贝当对象包含嵌套的对象或数组,你希望创建一个完全独立的副本,不希望原始对象的修改影响到副本。

优缺点

深拷贝

优点

  • 完全独立: 深拷贝生成的对象是原始对象的完全独立副本,对新对象的修改不会影响原始对象,避免了引用关系导致的副作用。
  • 适用于复杂对象: 对于包含嵌套对象、循环引用等复杂结构的对象,深拷贝能够完整复制整个对象树,保持数据的完整性。

缺点

  • 性能开销: 深拷贝涉及递归复制整个对象树,性能开销相对较大,特别是在处理大型对象时。

  • 实现复杂: 实现一个通用且高效的深拷贝函数相对复杂,需要处理循环引用、特殊对象类型等情况,避免出现死循环或遗漏某些情况。

浅拷贝

优点

  • 性能较好: 浅拷贝通常比深拷贝的性能更好,因为它只复制对象的第一层属性,避免了递归复制整个对象树的开销。

  • 简单易实现: 浅拷贝的实现方式相对简单,可以使用一些内建的方法或运算符(如Object.assign()、spread operator)来快速实现。

缺点

  • 嵌套对象问题: 浅拷贝对于嵌套对象的处理较为简单,如果对象内部还有对象引用,多个对象可能共享相同的嵌套对象,导致修改其中一个对象的嵌套对象时,其他对象也受到影响。
  • 不适用于特定场景: 在需要创建对象的完全独立副本、避免引用关系的场景下,浅拷贝的特性可能无法满足需求。

如何选择浅拷贝或深拷贝?

  • 性能要求: 如果性能是关键因素,而且对象结构较为简单,可以选择浅拷贝以提高性能。
  • 数据结构: 如果对象包含复杂的嵌套结构,循环引用或其他特殊情况,深拷贝更适合确保数据的完整性和独立性。
  • 修改原始对象: 如果不希望修改原始对象时影响到拷贝对象,使用深拷贝。
  • 简便性: 如果只需要复制对象的第一层属性,并且不关心嵌套对象的引用关系,可以选择使用浅拷贝。

在实际开发中,根据具体情况选择适当的拷贝方式,平衡性能和数据完整性,确保满足项目的需求。

文章来源:https://blog.csdn.net/qq_43774332/article/details/135271036
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。