一、浅拷贝和深拷贝的概念
首先,浅拷贝和深拷贝概念是相对于引用类型而言的。
JS中的引用类型:对象(数组、函数、Map、Set、Date)等
顶层值:相对于对象就是自有属性及方法,相对于数组对象就是第一维的元素,比如arr[0]
底层值:相对于对象就是原型链上的属性及方法,相对于数组对象就是多维的嵌套元素,比如arr[0][0]或arr[1][2][3]
1、浅拷贝(顶层独立,底层共享)
对顶层( 浅 层)元素进行复制,而对底层进行引用复制,与源对象共享相同的引用(指向相同的底层值)。
- 当修改源对象或者副本顶层值时,不会引起另一个对象发生改变。
- 当修改源对象或者副本底层值时,会引起另一个对象发生改变。
2、深拷贝(顶层和底层均独立)
无论是顶层值还是底层值,均不和源对象共享相同的引用。
所以,无论修改源对象或者副本的底层值还是顶层值均不会引起其他对象的改变
3、引用赋值(顶层和底层均共享,本就是同个东西)
相当于给同一个东西起了另一个名字而已,无论是顶层值还是底层值均是共享的。
无论修改源对象还是修改别名,其实修改的就是同一个东西,所以会引起其他对象的改变。
4、三者关系
操作 | 引用赋值 | 浅拷贝 | 深拷贝 |
---|---|---|---|
顶层值关系 | 共享(相互影响) | 独立 | 独立 |
深层值关系 | 共享 | 共享 | 独立 |
与源对象相等性 | true | false | false |
根据相等性其实也可以看出:
- 引用赋值的变量其实就是源对象的别名。
二、JS中的浅拷贝和深拷贝操作
1、浅拷贝操作
在 JavaScript 中,所有标准内置对象复制操作(扩展语法
、Array.prototype.concat()
、Array.prototype.slice()
、Array.from()
和 Object.assign()
)都创建浅拷贝。
数组对象复制操作有:
let arr=[1,2,3,4,5]
let nums1=[...arr];
let nums2=Array.from(arr);
let nums3=arr.slice();
let nums4=arr.concat();
let nums5=arr.filter(()=>true);
let nums6=arr.flat();
let nums7=arr.flatMap((a)=>a);
let nums8=arr.map((a)=>a);
let nums9=arr.toSpliced();
由于数组对象的复制操作均是浅拷贝
,所以对二维数组或者多维数组底层值的直接修改,均会使原数组也发生变化
let arr2d=[[1,2],[3,4]];
let nums=Array.from(arr2d);
nums[0][0]=100;
console.log(nums); //[[100,2],[3,4]]
console.log(arr2d); //[[100,2],[3,4]]
2、深拷贝操作(针对可序列化对象)
JSON.parse(JSON.stringify())
let arr2d=[[1,2],[3,4]];
let nums=JSON.parse(JSON.stringify(arr2d));
nums[0][0]=10;
console.log(nums); //[[10,2],[3,4]]
console.log(arr2d); //[[1,2],[3,4]]
structuredClone()
let arr2d=[[1,2],[3,4]];
let nums=structuredClone(arr2d); //此方法为web api方法
nums[0][0]=10;
console.log(nums); //[[10,2],[3,4]]
console.log(arr2d); //[[1,2],[3,4]]
3、引用赋值操作
就是普通赋值操作
let arr2d=[[1,2],[3,4]];
let nums=arr2d;
nums[0][0]=1000;
console.log(nums); //[[1000,2],[3,4]]
console.log(arr2d); //[[1000,2],[3,4]]
三、怎么理解浅拷贝和深拷贝是相对于引用类型而言的?
因为引用类型才会有顶层值和底层值,原始类型并不能嵌套,没有顶层值,更没有底层值。
那么原始类型是什么拷贝?答:相互独立的拷贝。
原始类型:Number, String, Boolean, null, undefined。
Symbol和BigInt也不是引用类型,它们的拷贝也是相互独立的。