引用数据类型
首先介绍一下引用数据类型,为什么叫‘引用数据’,可以理解为他的名称(索引)存在栈,他的具体值存在堆,并且这个名称(索引)指向他的值在堆中的地址,所以叫引用。
再来介绍一下深浅拷贝:
浅拷贝
所谓浅拷贝 比如变量a是一个引用数据类型,再来一个变量b,b去拷贝a,b拷贝的是a的引用地址,而并非堆里面的值。如果a要修改值,因为b的地址也指向这个值,所以b也会被修改
let a = [1,2,3,4] // 定义引用数据a
let b // 定义变量b
b = a // '='浅拷贝
a[0] = 10 // 修改a
console.log(a,b);
//a: [10, 2, 3, 4] b: [10, 2, 3, 4]
深拷贝
比如变量a还是一个引用数据类型,还是用b去拷贝a,b会在堆里新开辟空间,存放一个和a的值一模一样的值,并指向这个值,此时a和b互相独立只是值一样而已,修改也不会影响对方
let a = [1,2,3,4] // 定义引用数据a
let b // 定义变量b
b = [...a] // 拓展运算符深拷贝
a[0] = 10 // 修改a
console.log(a,b);
//a: [10, 2, 3, 4] b: [1, 2, 3, 4]
使用拓展运算符是因为比较好展示这个深拷贝的实现,b首先是等于=一个数组 '[]',其次数组中才是 ...a。
顺带提一下拓展运算符:它的基本用法是拆解数组和字符串
let a = [ 1, 2, 3, 4 ]
let c = 'holle'
console.log( ...a );
console.log( ...c );
// 1 2 3 4
// h o l l e
而且拓展运算符和一些数组和对象的方法比如 Object.assign slice concat,都可以进行深拷贝,但是这仅限于需要拷贝的数组或对象只有一层,也就是数据或对象中的值都是基本数据类型
let a = [ 1, 2, 3, [ 4, 5 ], function(){} ]
let b = [...a]
a[0] = 10
a[3][0] = 40
console.log( a );
console.log( b );
在拓展一现JSON,通过先转字符串,再转对象或数组即可实现深拷贝,但是如果这个对象或数组中的某个值是方法的话,转换后将会是null
let a = [ 1, 2, 3, [ 4, 5 ], function(){} ]
let b = JSON.parse(JSON.stringify(a))
a[0] = 10
a[3][0] = 40
console.log( a );
console.log( b );
如果想实现有多层有函数的深拷贝最好参考参考大佬们的递归拷贝,因为在实战中没有遇到过这种需求,如果有好的实现方法欢迎留言讨论