讲讲深拷贝、浅拷贝,一起手撕深拷贝
在《眼泪女王》中,男主角与女主角通过打印机相识、相爱、相知,让一个高材生嫁入豪门。那么今天,我相信我们也可以通过拷拷贝,成为好朋友。让我们走进今天的主题,深拷贝与浅拷贝。
一、变量在内存中的存储
- 原始类型:number、string、boolean......这些类型的值直接存储在变量的栈堆内存中,放你声明一个原始类型的变量并赋值时,实际上就直接存储在该变量的内存位置上。
let num = 10
let str = 'hello'
let bool = true
在这些变量声明并赋值后,会在相应的作用域的执行上下文中开辟空间(栈内存)存储它们。这是因为原始数据类型的数据量相对较小,可以直接在栈内存中快速分配与释放。
- 引用类型:object、array、function......这些类型里面可能会嵌套其他的引用类型,不是直接存储在变量的内存位置上,而是存储在堆内存中。
这些变量由于可能存在嵌套关系,数据量可能会很大,所以它实际存在堆内存中,在相应的执行上下文的栈内存中,存储的实际上是一个指向堆内存中的指针或引用。
理解了这些后,拷贝简直轻而易举!
二、浅拷贝
只拷贝对象的第一层属性和值,也就是说,如果对象是引用类型,那么它只会拷贝这个引用,而不是引用指向的内容,所以拷贝出来的新对象,在修改引用类型的值时,会修改到原对象的值。
浅拷贝的几种方法
let obj2 = Object.create(obj)
let obj = {
b:[1,2,3],
a: 1
}
let obj2 = Object.create(obj)
obj.b.push(4)
obj2.a = 2
console.log(obj2.a);
console.log(obj2.b);
console.log(obj);
以obj作为原型对象,新对象继承了obj的这些属性,但是对于引用类型,只会继承引用地址,通过修改原对象的属性,新对象属性也会被修改。
let obj2 = Object.assign({},obj)
let obj = {
b:[1,2,3],
a: 1
}
let obj2 = Object.assign({},obj)
obj.b.push(4)
obj.a = 2
console.log(obj2);
console.log(obj);
直接将空对象与原对象合并,返回一个新对象,同样只修改到了引用类型。[].concat(arr)
,也是类似的效果。
- 反转再反转
arr.toReversed().reverse()
let arr = [1,2,3,4,{a:2}]
let arr2 = arr.toReversed().reverse()
arr[4].a = 3
console.log(arr2);
最好理解的一种,哈哈哈,全面一点,勉强加入。还有一个arr.slice(0)
,也是类似的“赖皮”方法,这里就不多说啦。
[...arr]
数组解构
let arr = [1,2,3,4,{a:2}]
let arr2 = [...arr]
arr[4].a = 3
console.log(arr2);
三、 深拷贝
不仅仅拷贝对象的第一层属性和值,还会递归的拷贝对象的所有层级,包括引用类型的属性。这样就可以做到原对象与拷贝对象的引用类型属性会各自指向不同的内存地址。
JSON.parse(JSON.stringify(obj))
let obj = {
a:1,
b:{n:2},
c:'cc',
d:true,
e:undefined,
f:null,
g:function(){},
h:Symbol(1),
// i:123n
}
let newobj = JSON.parse(JSON.stringify(obj))
console.log(newobj);
console.log(obj);
无法识别bigInt类型,无法拷贝undefined、function、symbol。
TypeError: Do not know how to serialize a BigInt
const newObj = structuredClone(obj)
let obj = {
a:1,
b:{n:2}
}
const newObj = structuredClone(obj)
obj.b.n = 3
console.log(newObj);
相对较新的API,接收任何可以被结构化克隆算法处理的值(对象、数组、Map、Set等),返回一个完全独立的拷贝。
End:手写深拷贝
let obj = {
a:1,
b:{n:2}
}
function deepCopy(obj){
let newObj = {}
for(let key in obj){
if(obj.hasOwnProperty(key)){
if(obj[key] instanceof Object){ //typeof obj[key] =='obeject' && obj[key]!=null
newObj[key] = deepCopy(obj[key]) //递归判断
}else{
newObj[key] = obj[key]
}
}
}
return newObj
}
let obj2 = deepCopy(obj)
obj.b.n = 20
console.log(obj2);
既然浅拷贝只会帮我们拷贝第一层,那我们手动递归找到它的最里层为对象吗,而不是一个引用地址,然后进行拷贝操作。用obj.hasOwnProperty(key)确保只处理对象自己的属性,而不原型链上的。
现在我们可以成为好朋友了嘛,欢迎下次再见。
转载自:https://juejin.cn/post/7386958406135971890