参考文章:
数据结构和数据存储方式
在聊这个问题之前,我们得先捋一捋JavaScript的数据结构和数据存储方式,JavaScript的数据结构主要分两种。
1.基本数据类型(六种):String、Number、Null、Underfined、Boolean、Symbol;
2.引用数据类型(三种):Object、Array、Function;
- 基本数据类型的存储是key和value都存储在栈内存中。
例如let a = 1;
当你用b=a复制a时,会另外开辟了新内存空间存储b。
你此时修改a和b的值,也互相不会影响。
- 而引用类型的存储是key在栈内存中,但是value在堆内存中,栈内存仅仅是存储了堆内存的地址
例如let aArr = [1,2,3,4,5];
这个时候使用bArr=aArr复制aArr时,的确也是开辟了新内存空间存储bArr,但是问题在于,这个时候栈内存中的value值复制给bArr以后,这个只是一个地址罢了,并非具体的值,而这个地址同样的指向了同一个堆内存存储的值。好比一个房子在一街一号,然后另外一个房子也在一街一号,那这俩房子实际上就是同一个房子。
深拷贝和浅拷贝
深拷贝和浅拷贝只是针对Array和Object这种复杂的数据结构来说,对基本数据类型是没有这种说法的。
常见的浅拷贝方法有:
1.Object.assign()
2.Array.prototype.concat()
3.Array.prototype.slice()
常见的深拷贝方法有:
1.JSON.parse(JSON.stringify())
2. 递归
浅拷贝
看到下面这个例子,你是不是以为Object.assign()是深拷贝?你只看到了第一层,而它在大气层。(手动狗头)
当object的属性只有一层的时候,Object.assign()是深拷贝,这是因为该对象中属性的值都是基本数据类型,也就是上面讲到的都存储在栈中,拷贝过去的时候就是直接复制了属性的值,但是一旦出现像是下面这种数据结构,属性的值是引用类型Array或者是Object的时候就暴露了自己是个浅拷贝的事实。其他两种浅拷贝方法也是同理。
深拷贝
JSON.parse(JSON.stringify())转成JSON格式然后反转回来,这种方法固然简单方便,但是JSON是一个通用的文本格式,和语言无关。设想如果将函数定义也stringify的话,如何判断是哪种语言,并且通过合适的方式将其呈现出来将会变得特别复杂。特别是和语言相关的一些特性,比如JavaScript中的Symbol。诸如此类等等,所以坑非常多一不小心就会翻车,除非是数据结构特别简单一般不建议使用。更详细的说明可以参考 你所不知道的JSON.stringify
递归的思路就是不断的往下查询,直到属性的值的类型是基础数据类型即可。