浅谈深浅拷贝以及Object.assign和JSON.parse(JSON.stringify(obj))

深浅拷贝是因为计算机对js不同数据类型存储方式不同才引申出来的

对于基本数据类型,存储在栈中,在复制基本数据类型的时候,会在栈中开辟新空间,将值复制给新的变量(对于基本数据类型来说都是深拷贝),修改旧的变量不会影响新的变量。

而对于引用数据类型的值是在堆中存储,栈中存储的是对堆中实际数据的引用(指针),在复制引用数据类型时,会将对堆中的引用复制给新的变量(也就是浅拷贝),因此,新的变量和旧的变量保存的都是对同一个堆的引用,所以在修改堆中的数据时,新的变量也会跟着修改,因此引申出深拷贝的概念。

在日常使用中比较常见的深拷贝的方式:

1.JSON.parse(JSON.stringify(obj))

这种使用方式对于简单的数据类型(数组,对象)来说完全够用,但是需要注意的是,function ,undefined,等,会导致序列化时丢失数据,而Date类型的则会转成字符串,NaN则会转成null,(Symbol和正则也会有问题)

    let obj = {
      a: 1,
      b: 2,
      c: [1, 2],
      d: function () {
        console.log("test");
      },
      e: undefined,
      f: null,
      g:new Date,
      h:NaN
    };
console.log(JSON.stringify(obj))
// {"a":1,"b":2,"c":[1,2],"f":null,"g":"2024-08-26T14:59:41.359Z","h":null}

除此之外循环引用也会导致序列化失败报错

let obj = {};
obj.self = obj;
let copy = JSON.parse(JSON.stringify(obj));  // 会抛出错误 "TypeError: Converting circular structure to JSON"

2.使用专门的库(如 Lodash 的 _.cloneDeep)

这个不多赘述,自己写深拷贝要考虑的情况太多,递归(考虑到二维数组等的情况),数据类型的判断等,重复造轮子的事就不必干了

再说一个

Object.assign()

Object.assign 进行的是浅拷贝,而不是深拷贝
有个列子可以看下

    let obj1 = { a: { b: 1 } };
    let obj2 = Object.assign({}, obj1);
    obj1.a = {c:1};
    console.log(obj1, obj2);

此时打印出

{ a: { c:1 } }    { a:{ b:1 } }

有的人认为这不是也没改变吗,这不就是深拷贝,其实不然,正是因为是浅拷贝(obj1.a和obj2.a是同一个堆得引用)才会导致这种情况

因为obj1.a = {c:1} 这一步 会将新的堆的引用赋值给obj1.a 而obj2.a因为和之前obj1.a引用的是一个地址,所以不会改变
再看下面的例子

    let obj1 = { a: { b: 1 } };
    let obj2 = Object.assign({}, obj1);

    obj1.a.b = 2;
    obj2.a.b; // 2

此时的obj2.a.b 的值就是2 ,恰巧说明了Object.assign 进行的是浅拷贝,而不是深拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值