谈一谈浅拷贝和深拷贝

深拷贝和浅拷贝是两种JavaScript中常见的变量赋值方式。假设有一个对象obj,它包含了若干属性和方法,请看下面的讲解:

  1. 浅拷贝
    在对象的浅拷贝中,新对象只是简单地复制原始对象在内存中存储的地址,而不是其实际数据。也就是说,新对象的改变会影响到原始对象。
    例如,当使用Object.assign()函数或…操作符时,它只复制了原始对象可枚举的自身属性,未能深递归到子对象中。
    下面是一个针对对象的浅拷贝代码示例:
// 定义一个原始对象
let obj = { a: 10, b: 20 }

// 浅拷贝至新对象
let newObj = Object.assign({}, obj);

// 改变新对象中的属性值
newObj.b = 30;
console.log(obj.b); // 20
console.log(newObj.b); // 30

从示例中可以看到,虽然已更改“newObj”对象的“b”属性,但并未影响到原始对象中的值。
2. 深拷贝
相反地,在深拷贝中,当执行对象之间的复制时,每个属性会被递归地拷贝,新的对象被分配了一个不同的内存地址,这表示在新对象上的更改不会影响原始对象。
例如,可以使用JSON(JavaScript Object Notation)内置函数JSON.parse()和JSON.stringify()将对象深拷贝到新对象中。但该方法也有限制, 无法复制一些 JS 对象特性(如 function, Infinity 等), 以及内置对象本身(如 Date, Map等)。
下面是一个针对对象的深拷贝代码示例:

// 定义一个原始对象
let obj = { a: 10, b: { c: 20 } }

// 深拷贝至新对象
let newObj = JSON.parse(JSON.stringify(obj));

// 改变新对象中的属性值
newObj.b.c = 30;
console.log(obj.b.c); // 20
console.log(newObj.b.c);

或通过递归实现

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') {
    // 如果是null、undefined、字符串、数字、布尔值等基本类型数据,直接返回
    return obj;
  }

  let clone = Array.isArray(obj) ? [] : {}; // 判断是数组还是对象

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) { // 判断是否为自身属性
      clone[key] = deepClone(obj[key]); // 递归进行深拷贝
    }
  }

  return clone;
}

该函数首先判断传入的参数是否为null或基本类型,如果是直接返回该值;否则就创建一个新的对象或数组,然后遍历原对象或数组的每个属性/元素,并递归地复制其对应的值到新数组或对象中。
需要注意的是,在进行深拷贝时,我们使用了hasOwnProperty来判断是否为自身属性,这是因为JavaScript中所有对象都继承自Object.prototype,并在原型链上定义了一些可枚举的属性。所以如果不使用hasOwnProperty,函数有可能会复制到原型对象上的属性。

深拷贝的循环引用问题

当以上深拷贝遇到循环引用时,会发生无限递归的情况,如:

const obj = {
  arr: [1, 2, 3],
  a: 4
};
obj.sub = obj

obj.arr.push(obj)

此情况如果使用上述深拷贝,会出现无限递归。
解决方法:可以使用缓存来解决。
具体思路:在外部定义一个存放缓存的变量,来缓存克隆的map结构,当判断拷贝的对象时对象类型时,读取缓存是否包含此对象,如有直接return出去,如果没有则添加。这样就可以解决循环引用时,发生无限递归的问题了。

function deepClone(value) {
  // 定义缓存
  const cache = new WeakMap() //最好不要使用map() 来克隆结构,会影响垃圾回收.而WeakMap 不影响
  function _deepClone(obj) {
    if (obj === null || typeof obj !== 'object') return obj
    // 判断是否存在
    if (cache.has(obj)) {
      return cache.get(obj) //存在 return 
    }
    let clone = Array.isArray(obj) ? [] : {};
    // 不存在,添加
    cache.set(obj, clone)
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        clone[key] = _deepClone(obj[key])
      }
    }
    return clone
  }
  return _deepClone(value)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值