带你深入理解对象拷贝:浅拷贝与深拷贝的区别与应用

185 阅读5分钟

引言:

在编程中,我们经常需要处理对象拷贝的场景。对象拷贝涉及到原对象和拷贝对象之间的关系,特别是在处理引用类型时需要格外注意。本文将深入探讨浅拷贝和深拷贝这两种常见的对象拷贝方式,包括它们的概念、区别以及适用场景。

一、浅拷贝

浅拷贝是指创建一个新对象,并将原对象中的属性值复制到新对象中。但需要注意的是,如果原对象的属性值是引用类型,浅拷贝只会复制引用,而不会复制实际的数据内容。这意味着原对象和拷贝对象之间共享同一份数据,对原对象的修改会影响到拷贝对象。

常见的浅拷贝方法有:

  1. Object.create(obj):通过创建一个新对象,并将原对象作为新对象的原型,实现浅拷贝。
// 原对象
let A = {
    name : '小白',
    hobby: {
        n: '跑步'
    }
}
// 使用Object.create() 进行浅拷贝
let copy = Object.create(A)
// 打印拷贝对象
console.log(copy.name,  copy.hobby); // 小白 { n: '跑步' }

// 修改原对象
A.name = '小黑'
A.hobby.n = '跳舞'

console.log(A); // { name: '小黑', hobby: { n: '跳舞' } }
console.log(copy.name,  copy.hobby); // 小黑 { n: '跳舞' }
// 通过打印结果,我们可以知道,原对象的修改会影响到拷贝对象

  1. Object.assign({}, obj):使用Object.assign()方法将原对象的属性复制到空对象中,实现浅拷贝。
// 还是上面那个对象
let copy = Object.assign({}, A)
console.log(copy); // { name: '小白', hobby: { n: '跑步' } }
// 修改原对象
A.name = '小黑'
A.hobby.n = '跳舞'

console.log(A); // { name: '小黑', hobby: { n: '跳舞' } }
console.log(copy); // { name: '小白', hobby: { n: '跳舞' } }
// 易知,虽然这个方法对于原始类型来说,原对象的修改不能影响拷贝对象,好像是深拷贝,但是对于引用类型来说,还是会受到影响,,所以还是浅拷贝
  1. [].concat(arr):通过连接原数组和空数组,生成一个新的数组,实现浅拷贝。
  2. 数组解构:使用数组解构语法[...arr]创建一个新的数组,并将原数组的元素复制到新数组中。
  3. arr.toReversed().reverse(),这段代码的意义是将一个数组进行反转,并再次进行反转,从而实现浅拷贝
// 3,4,5点实例
let arr = [1, 2, 3, {n: 10}]
let Arr1 = [].concat(arr)
let Arr2 = [...arr]
let Arr3 = arr.toReversed().reverse()
arr[3].n = 100

console.log(Arr1); // [ 1, 2, 3, { n: 100 } ]
console.log(Arr2); // [ 1, 2, 3, { n: 100 } ]
console.log(Arr3); // [ 1, 2, 3, { n: 100 } ]
// 对于属性为引用类型,这三个方法也只能拷贝引用,而不会拷贝里面的实际内容

二、深拷贝

深拷贝是指创建一个全新的对象,并递归地复制原对象的所有属性,包括嵌套的对象和数组。深拷贝后,新对象和原对象完全独立,互不影响。即使对原对象进行修改,也不会影响到拷贝对象。

常见的深拷贝方法是使用JSON.parse(JSON.stringify(obj))。这种方法通过将原对象转换为字符串,再将字符串转换回对象,实现深拷贝。

let obj = {
    name: '小白',
    age: 18,
    a: {
      n: 1
    },
    b: undefined,
    c: null,
    d: function() {},
    e: Symbol('hello'),
  }
  
  let obj2 = JSON.parse(JSON.stringify(obj))
  obj.a.n = 11
  console.log(obj);
  console.log(obj2);
  // 打印结果:
  {
  name: '小白',
  age: 18,
  a: { n: 11 },
  b: undefined,
  c: null,
  d: [Function: d],
  e: Symbol(hello)
 }
{ name: '小白', age: 18, a: { n: 1 }, c: null }
// 由结果知,虽然确实是深拷贝,但是有些数据类型却无法处理

需要注意该方法存在一些缺陷。首先,它无法处理特殊数据类型如undefined、function和Symbol。其次,当原对象存在循环引用时,即对象内部某个属性引用了对象自身,使用该方法会导致错误。

三、选择适当的拷贝方式 在实际开发中,我们需要根据具体需求来选择合适的拷贝方式。

浅拷贝适用于对简单对象进行拷贝,并且不关心原对象和拷贝对象之间的关联性。它具有简单、快速的特点,但需要注意对于引用类型属性的修改会影响到拷贝对象。

深拷贝适用于对复杂对象进行拷贝,并且希望实现原对象和拷贝对象之间的完全独立。它可以处理嵌套对象和数组,并确保对原对象的修改不会影响到拷贝对象。然而,需要注意深拷贝方法的局限性,特别是在处理特殊数据类型和循环引用时。

在实际应用中,如果需要更强大和灵活的拷贝功能,可以考虑使用第三方库,如lodash的_.cloneDeep()方法。这些库提供了更多选项以应对各种复杂情况。

结论: 对象拷贝是编程中常见的操作,浅拷贝和深拷贝是两种常用的拷贝方式。

浅拷贝只复制引用,对原对象的修改会影响到拷贝对象,适用于简单对象的拷贝。

深拷贝创建全新对象,递归复制所有属性,原对象和拷贝对象完全独立,适用于复杂对象的拷贝。但需要注意深拷贝方法可能存在的缺陷。

根据具体需求选择合适的拷贝方式,或者借助第三方库提供的更强大拷贝功能,可以更好地处理对象拷贝操作,提高代码的健壮性和可维护性。

感谢观看!