美文网首页
彻底弄懂js的深浅拷贝

彻底弄懂js的深浅拷贝

作者: 变量只提升声明不提升赋值 | 来源:发表于2022-03-04 16:13 被阅读0次

    所谓的深浅拷贝其实就是引用数据类型在赋值是会出现的问题。
    js数据类型可以统分为两大类,第一类为基本数据类型,也就是String number 布尔等等.还有一类就是引用数据类型,也就是Object,两者有什么区别的。

    基本数据类型存储在栈内,引用类型存在堆内。

    基本数据类型在做赋值的时候会为变量重新开辟一个内存空间来存储。而引用类型在做赋值的时候则是复制了一个指针,指向当前数据在堆内的地址。

    这也就会引发修改了某个变量会影响到另一个变量的问题。

    看个例子:

    let a = {name:1}
    let b = a
    b.name = 2
    此时a的name属性也会变为2
    

    因为两者虽然是两个变量,但其实指向的都是同一个地址,故修改了a或者b的属性,另一个也会收到影响。
    那么如果直接给b或者a赋另外一个值,会不会影响到对方呢


    image.png

    答案是不会。因为赋值的时候已经给b开辟了一个新的内存空间,不再是在原地址上做修改了。

    上述那种赋值方法就是所谓的浅拷贝。

    在日常开发中,数据结构层层嵌套,这种浅拷贝显然是不可取的。那么就引出了新的处理方法,深拷贝

    所谓深拷贝,那自然是给变量也开辟一个新的内存地址,并让他拥有和目标对象相同的值。

    下面列举几个网上错误的做法。

    谣言1.es6的扩展运算符...

    有很多帖子都说深拷贝可以用...做到。但实则不然


    image.png

    看上面那个例子,是不是觉得...可以深拷贝引用类型。

    如果是这样认为那就被误导了。

    首先在上述例子中,b是一个独立的对象,这就从根本和a对象没有联系。也就是和直接赋值不一样。所以不存在b的内存和a的内存相同。其次,...运算符将a对象展开,并把里面的属性复制到b身上。a里面的name属性是个基本数据类型。所以此时b对象的name也会有一个新的内存空间。
    这个时候修改b的name属性。还会影响到a的name属性嘛?答案是不会的,两个name都是个独立的变量。

    再看个例子推翻...可以深拷贝这个说法


    image.png

    这个例子中,给a加了个age属性,是个对象,里面有个val属性。
    我们再用...来做一个拷贝。并且修改了name属性,也修改了age的val属性
    可以看到,name确实没有影响到a,但是age的val属性呢?是不是影响到了a的age对象。

    ...之所以会让人产生错觉可以深拷贝,那是因为恰好目标对象的属性都是基本数据类型。他都是重新做了个赋值而已。本质上他并不能深克隆引用类型,只是某些情况下恰巧被他做到了而已

    所以一定要认清扩展运算符到底在什么情况下才能深克隆引用数据类型。

    谣言2:数组的concat方法。
    concat是一个用来连接两个数组的方法。那么用他能不能做到深拷贝数组呢
    先看个例子


    image.png

    首先看个浅拷贝数组的例子---直接赋值
    接下来我们使用concat方法试一试


    image.png
    是不是又惊奇的发现,诶他好像可以深拷贝诶!
    其实还是跟扩展运算符一样。他也只能深拷贝基本数据类型。道理是差不多的就不过多赘述了。看一个反证例子
    image.png

    还有些文章说什么这两个方式只能拷贝引用类型的第一层属性。更深层的拷贝不了。那纯属扯淡。这两种方法压根就不能做深拷贝,只是被你碰巧遇上了正确结果。

    下面说一些真正的深拷贝方法。
    1、最最最基本的那自然就是递归遍历数据的属性,一个一个做赋值。遇到引用类型就继续遍历。这里就补贴方法了自行百度。
    2、最简单的方法:将目标数据转为json串,然后再转回来赋值。
    JSON.parse(JSON.stringify(xxx))


    image.png

    当然这种办法也是存在弊端得,比如对象上某些函数或者某些由构造函数创建得对象属性都会被抛弃。json只能将对象得可枚举属性序列化。由构造函数构造出来得对象会抛弃掉原型对象。导致他得某些属性不可用

    相关文章

      网友评论

          本文标题:彻底弄懂js的深浅拷贝

          本文链接:https://www.haomeiwen.com/subject/knhmrrtx.html