JavaScript技术回顾之深拷贝与浅拷贝

280 阅读5分钟

前言:

2020年,对所有人来说都是一个不平凡的一年.疫情,很沉重的一个词.到目前为止,很多省都保持0新增
.愿祖国一直健康下去...

4月份中旬,终于下定决心,五月份离职.辞职申请流程已经提交,本以为,带过项目,
负责公司前端大大小小的项目搭建,代码开发,公共组件构建,各种框架信手拈来,
一个面试而已,小case,应该没啥问题.
然而在前两天面试时的经历,却是迎头一棒,让我瞬间清醒,终究还是捡了芝麻,丢了西瓜.
很简单的几个问题,其中就有这篇文章准备讲的深拷贝与浅拷贝.而我没答上来,甚至很多基础都没答上来.

其实一切的框架都是浮云.对于前端来说,JavaScript才是根本.

重新回顾各种JavaScript基础.  

如何区分深拷贝与浅拷贝?

1、什么是深拷贝?什么是浅拷贝?

浅拷贝: 它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类
型,拷贝的就是基本类型的值;
        如果属性是内存地址(引用类型),拷贝的就是内存地址。即只复制对象空间而不复制资源

深拷贝: 它会创建一个新对象,给这个新对象新开一个内存地址,源对象与拷贝对象互相独立,
其中任何一个对象的改动都不会对另外一个对象造成影响
(上述解释来自百度百科)
看不懂?看不懂没关系,我们换一种方式来说。

浅拷贝可以理解为浅复制。同理,深拷贝可以理解为深复制

浅拷贝(浅复制):浅拷贝仅仅是复制一份复制体,但是指针(JS没指针,但是可以去这样理解)
              还是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象
              也会相应改变.
              浅拷贝实际上只复制一层对象,当对象的属性是引用类型时,实际复制的是其引用,
              当引用指向的值改变时也会跟着变化

深拷贝(深复制):深拷贝是复制一份复制体,在内存栈中新开一份地址,把复制体放进去。
              所以,复制体和被复制体之间完全独立,没有任何影响。可以复制对象的所有层级.

       举个例子,一个魔方放在桌子上,小明手里没有魔方,小明说:我有,
       我的魔方就是桌子上那个。小军去拿了一个魔方,调成和桌子上的魔方一样。
       小明有没有魔方?有,桌子上那个(指向桌子)。小军也有,自己有一个,
       如果我们把桌子上的魔方打乱顺序,小明的魔方顺序也跟着变了(浅拷贝),
       但是小军的没变(深拷贝)。
       如果我们把桌子上的魔方拿走了,小明的魔方就没了(空指针)。小军的还在(新
       开内存地址)

    所以,当我们区别深拷贝与浅拷贝时.如果被复制体改变了,复制体也跟着改变.或者只复制了一层对象,那么,
    我们就视作浅拷贝.
    如果复制了多层对象并且复制体不会跟随被复制体改变,那么,就可以视为深拷贝.


这个意思我们知道了,那么,作为程序员,还是上代码吧。眼见为实。
实现浅拷贝的方法:
方法一 直接赋值

当obj1修改时,obj2也跟着修改了

现在我们有这么一个需求,我们有了一个对象A,但是我想再有一个对象B,和对象A相互独立.此时,我们怎么做呢?

方法二for in ,forEach)

这里就完成了一个相互独立的浅拷贝.注意:这还是浅拷贝.为什么,只复制了一层对象. 上面的代码,我解释一下, 当我们循环赋值时,为什么用了forEach而不是直接for in 或者 for of 如果熟悉ESlint的话,那么会知道,此处用for in 是会报错的. 大概意思就是使用for..in会遍历整个原型链,这样不是很好的实现方法,推荐使用Object.keys. 什么是原型链,后续我们会继续写到.

方法三:ES6的Object.assign()方法

实际上,Object.assign()也是一个浅拷贝.

实现深拷贝的方法:

实现深拷贝,一般我们工作中常用的是JSON.stringify()方法;除此之外就是利用递归

方法一:JSON.parse(JSON.stringify())
    就是利用 JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象
    但是存在局限性,无法实现对对象中方法的深拷贝,会显示为undefined (这次面试就问到了这个,我没答对)
    所以一般此方法常用于处理json和数组的深拷贝

方法二: 利用递归实现深拷贝

方法三: 不闲麻烦的可以手动深复制(不推荐,这里不做多说明)
方法四 lodash函数库的_.cloneDeep()方法

let newObj = _.cloneDeep(obj)

除此之外,还能进行深拷贝的有,Array的slice()方法,concat()方法  Object的create()方法

示例:

    const arr = [1,2,3] 
    const arr1 = [4,5,6]
    
    const arr2 = arr.slice(0)
    arr2[0] = 999 ,
    有兴趣可以去打印一下arr 和 arr2的值
    
    这里说明一下, 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
    当数组里面的值是引用数据类型,属于浅拷贝
    
    const arr3 = arr1.concat()
    arr3[0] = 999
    
    
    还有一个ES6的扩展运算符,[...arr], 扩展运算符和slice一样,当数组里面的值是基本数据类型,
    比如String,Number,Boolean时,属于深拷贝,当数组里面的值是引用数据类型,属于浅拷贝

有些内容来自与百度,如有侵权,请联系删除.

学艺未精,往诸位看官指正.


不求与人相比,但求超越自己,要哭就哭出激动的泪水,要笑就笑出成长的性格!