谈一谈 JavaScript 中的拷贝原理

71 阅读2分钟

浅拷贝

浅拷贝:把对象拷贝给一个新的对象,开发中我们经常需要复制一个对象

如果直接赋值,则复制的是地址,修改任何一个对象,另一个对象都会变化

常见方法:

  1. 拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象
  2. 拷贝数组:Array.prototype.concat() 或者 [...arr]

当我们修改原始列表中的嵌套列表的元素时,拷贝后的列表也会受到影响。这是因为浅拷贝只复制了引用,而不是复制对象本身。

// 使用切片操作创建新列表
old_list = [1, 2, [3, 4]]
new_list = old_list[:]
print("Old List:", old_list)
print("New List:", new_list)

// 修改原始列表
old_list[2][0] = 5

// 查看结果,可以发现拷贝后的列表也受到了影响
print("Old List after modification:", old_list)
print("New List after modification:", new_list)

=====================================================

// 输出结果

Old List: [1, 2, [3, 4]]
New List: [1, 2, [3, 4]]
Old List after modification: [1, 2, [5, 4]]
New List after modification: [1, 2, [5, 4]]


浅拷贝注意:

  • 如果是基本数据类型拷贝值
  • 如果是引用数据类型拷贝的是地址

简单理解:如果是单层对象,没问题,如果有多层就有问题,还是会影响原来对象

深拷贝

深拷贝:拷贝多层,不再拷贝地址

常见方法:

  1. 通过 JSON 序列化实现
  2. lodash库 实现
  3. 通过递归实现

通过递归实现深拷贝

递归:

所谓递归就是一种函数调用自身的操作

  • 简单理解:函数内部自己调用自己, 就是递归,这个函数就是递归函数
  • 递归函数的作用和循环效果类似
  • 由于递归很容易发生“栈溢出”错误(stackoverflow),所以记得添加退出条件 return
// 递归实现深拷贝

function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    // 如果不是复杂数据类型则直接返回
    return obj;
  }

  let result = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    // 递归调用deepCopy方法进行深拷贝
    result[key] = deepCopy(obj[key]);
  }

  return result;
}

深拷贝思路:

  1. 深拷贝的核心是利用函数递归
  2. 封装函数,里面先判断拷贝的是数组还是对象
  3. 然后开始遍历
  4. 如果属性值是引用数据类型(比如数组或者对象),则再次递归函数
  5. 如果属性值是基本数据类型,则直接赋值即可