- 理解JS中的深拷贝和浅拷贝,必须知道如下两个知识点:
JavaScript
的数据类型
值类型(基本类型)
:字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)。
引用数据类型
:对象(Object)、数组(Array)、函数(Function)。
JavaScript
的数据类型的存储位置
值类型(基本类型)
:内存地址在栈(stack)中。该类型在栈的内存中会分配固定的内存空间。
引用数据类型
:内存地址存在堆(Heap)中。该类型在堆的内存中动态分配内存空间。
注意:引用数据类型
会分配一个指针变量在栈上,通过这个指针变量来操作堆中的数据。
在JS中操作的是栈上的指针变量,继而再找到堆中的内存地址。所以像如下=运算符
赋值的操作,arrA和arrB的内容都为["1","5"]
var arrA = ["1","2"]; var arrB = arrA; arrB[1] = "5";
知道了如上两个知识点,我们来看看JS中的深拷贝和浅拷贝
- 深拷贝是在栈中增加了一个指针变量的同时,在堆中对原有内存地址进行了拷贝,生成了一个新的内存地址,并且新增的指针变量保持着对新内存地址的引用,因此结果是
堆中的两个不同的内存地址在栈中分别都有一个指针变量
。- 浅拷贝是在栈中增加了一个指针变量,该指针变量指向原来的堆中的内存地址,并没有增加新的内存地址,因此结果是
栈中的两个指针变量指向的都是同一块内存地址
。- 在JS中,深拷贝和浅拷贝说的就是
引用数据类型
中的对象(Object)、数组(Array),和值类型(基本类型)
没关系。- 在JS中,简单来说
深拷贝
:数组B拷贝了数组A,数组B修改了,数组A不会修改;而浅拷贝
:数组B拷贝了数组A,数组B修改了,数组A不会修改。
js中的扩展运算符(...
)、 concat 、 slice 是深拷贝还是浅拷贝?
concat
、slice
以及 es6扩展运算符(...
)仅仅是对第一层数据进行的是深拷贝,而对第二层、第三层....第N层进行的是浅拷贝。
简而言之,使用concat
、slice
以及 es6扩展运算符(...
)操作后的数据,数据中是基本类型
的元素,该元素是深拷贝得到的;数据中是是引用数据类型
的元素,该元素是深拷贝得到的。
例如,["1","2",{id:"3"},["4","5"]]
是被 es6扩展运算符(...
)操作后的数据,那么元素"1"
和"2"
是深拷贝得到的,{id:"3"}
和["4","5"]
是浅拷贝得到的,修改{id:"3"}
和["4","5"]
,原数据的{id:"3"}
和["4","5"]
也会被修改。
拿普通的=运算符
和concat
、 slice
以及 es6扩展运算符(...
)来举例:
1.抛砖引玉:普通的=运算符
对 对象(Object)、数组(Array)
的拷贝为完全的浅拷贝
无嵌套的普通数组:
完全的浅拷贝(第一层到第N层)
image.pngvar arrA = ["1","2"]; var arrB = arrA; arrB[1] = '5'; console.log('arrA为--',arrA,'arrB为--',arrB);
数组Array嵌套了字典对象Object及数组Array:完全的浅拷贝(第一层到第N层)
image.pngvar arrA = ["1","2",{id:"3"},["4","5"]]; var arrB = arrA; arrB[1] = '5'; arrB[2].id = '6'; arrB[3][1] = '7'; console.log('arrA为--',arrA,'arrB为--',arrB);
字典对象Object嵌套了字典对象Object及数组Array:完全的浅拷贝(第一层到第N层)
image.pngvar dicA = {id1:"1",id2:"2",id3:{idx:"3"},id4:{idx:["4","5"],idy:["6","7"]}}; var dicB = dicA; dicB.id2 = '8'; dicB.id3.idx = '9'; dicB.id4.idy[1] = '10'; console.log('dicA为--',dicA,'dicB为--',dicB);
2.扩展运算符 ...
无嵌套的普通数组:
深拷贝
image.pngvar arrA = ["1","2"]; var arrB = [...arrA]; arrB[1] = '5'; console.log('arrA为--',arrA,'arrB为--',arrB);
数组Array嵌套了字典对象Object及数组Array:第一层深拷贝,第二层到第N层浅拷贝
image.pngvar arrA = ["1","2",{id:"3"},["4","5"]]; var arrB = [...arrA]; arrB[1] = '5'; arrB[2].id = '6'; arrB[3][1] = '7'; console.log('arrA为--',arrA,'arrB为--',arrB);
字典对象Object嵌套了字典对象Object及数组Array:第一层深拷贝,第二层到第N层浅拷贝
image.pngvar dicA = {id1:"1",id2:"2",id3:{idx:"3"},id4:{idx:["4","5"],idy:["6","7"]}}; var dicB = {...dicA}; dicB.id2 = '8'; dicB.id3.idx = '9'; dicB.id4.idy[1] = '10'; console.log('dicA为--',dicA,'dicB为--',dicB);
3. concat 连接两个或多个数组
无嵌套的普通数组:
深拷贝
image.pngvar arrA = ["1","2"]; var arrB = arrA.concat(); arrB[1] = '5'; console.log('arrA为--',arrA,'arrB为--',arrB);
数组Array嵌套了字典对象Object及数组Array:第一层深拷贝,第二层到第N层浅拷贝
image.pngvar arrA = ["1","2",{id:"3"},["4","5"]]; var arrB = arrA.concat(); arrB[1] = '5'; arrB[2].id = '6'; arrB[3][1] = '7'; console.log('arrA为--',arrA,'arrB为--',arrB);
4.slice 数组截取
无嵌套的普通数组:
深拷贝
image.pngvar arrA = ["1","2"]; var arrB = arrA.slice(); arrB[1] = '5'; console.log('arrA为--',arrA,'arrB为--',arrB);
数组Array嵌套了字典对象Object及数组Array:第一层深拷贝,第二层到第N层浅拷贝
image.pngvar arrA = ["1","2",{id:3},["4","5"]]; var arrB = arrA.slice(); arrB[1] = '5'; arrB[2].id = '6'; arrB[3][1] = '7'; console.log('arrA为--',arrA,'arrB为--',arrB);
js中执行深拷贝的两种做法:
- json对象转成json字符串
JSON.stringify()
,json字符串转json对象JSON.parse()
- 递归算法对每个层级都进行一次深拷贝
网友评论