群友:听说你最近在刷面试题?要不我问一道题你看看你能不能回答?
我:没问题,我对面试八股文多少还是有点自信的。
群友:那,解构赋值到底是深拷贝还是浅拷贝?
我:你他妈是故意来找茬的吧?
正文
从来没想过的基础问题,一下子被干懵了,我个人在入行第二年的时候就通读了《ES6标准入门》这本书。
关于解构赋值这里,因为用的不熟悉所以特别多看了两眼,我记得文中从来没提过这事儿,而且我也从来没想过这茬子事情。
哎,这种题作为面试题,真把人干躺了也无所谓,就当是吃个经验教训。
深浅拷贝
明确一下深拷贝和浅拷贝的定义,或者说深拷贝和浅拷贝所应用于的数据类型。
- 深拷贝:修改新变量的值不会影响原有变量的值。默认情况下基本数据类型(number,string,null,undefined,boolean)都是深拷贝。
- 浅拷贝:修改新变量的值会影响原有的变量的值。默认情况下引用类型(object)都是浅拷贝。
其实你只要理解透彻了这两句话就应该明白了解构赋值,甚至深拷贝的原理;
写两个例子,理解一下深拷贝和浅拷贝;
基本数据类型
我们平时开发中,这种情况很常见的,直接用等号赋值就是深拷贝,如果没有新的内存空间存放变量,我们岂不是会直接修改元数据?
因此,此时对b的数值的修改并未影响a,所以基本数据类型赋值就是深拷贝。
1 | let a = 1; |
引用数据类型
所谓深浅拷贝,核心就是要不要用新的存储空间来存放变量,而且平时也主要是针对引用类型的数据。
这里直接用等号赋值,我们会发下,是浅拷贝赋值。
1 | let a = { |
其实这个时候,我们大概就能想明白一件事儿,那就是一门语言在面对这种大小不确定的引用类数据类型,底层原理上肯定是倾向于浅拷贝。
不然,各个都是深拷贝,那就得开辟大量的新空间用来存储,一旦全面铺开,将会有一笔很大的存储空间消耗。
如果是这样,那这门语言一定会得到最消耗内存的烂名声,任何一名语言设计者在设计之初的时候,肯定不会希望自己设计的语言大量消耗存储空间。
所以,大多语言,引用类型数据的默认赋值方式,大概率都会是浅拷贝。
解构赋值
在上边赋值的时候,我们已经注意到了,
ES6的解构赋值,大家应该都清楚,本质上其实就是一种语法糖,可以快速取出数组或者对象中的值,这里放个例子。
1 | let a = { |
发现a的数据并没有被改变,解构赋值好像是深拷贝啊?????
我们再改一下上面的例子看看:
1 | const a = { |
发现解构赋值出来的对象将原对象a中的addr的数据修改了,这样看还是浅拷贝;
这里我们根据上述情况,可以简单的
解构赋值,如果所解构的原对象是一维数组或对象,其本质就是对基本数据类型进行等号赋值,那它就是深拷贝;
如果是多维数组或对象,其本质就是对引用类型数据进项等号赋值,那它就是浅拷贝;
最终的结论就是:解构赋值是浅拷贝(因为它确实不能对多维数组或对象达到深拷贝的作用);
深拷贝本质
这里我们放一套常规的深拷贝赋值方法。
1 | function deepClone(source){ |
你发现了吗?其实深拷贝本质上还是将对象拆开为基本数据类型进行赋值。
所以,解决问题的本质就是让复杂问题简单化,所谓的深拷贝,本质上无非就是加强版的基础类型赋值罢了。
1 | // 如果不是,就直接赋值 |
结语
虽然是非常基础的一个问题,但是却让我颇有受益。
所有语言开发之初,肯定对相关问题都有过优化,如今项目工程越来越繁杂,这种曾经精妙的设计确实也落入了尘埃,到底也是时代的眼泪了。
当然,感叹归感叹,但依然是实际开发中用不到的知识,不过确实有趣。