深拷贝和浅拷贝
在说深拷贝和浅拷贝之前,我们先弄清一个问题,我们的数据储存在内存的哪呢,这里就需要说到堆
、栈
的概念,一般来讲内存中数据都可以key和value
键值对储存,而值类型因为较小,一般就直接放在了栈中,而对于引用类型
,可能一个引用类型
的数据量非常大,设计者考虑空间的问题就把对象的value放在了堆中,这样栈中的其key对应value则存放了数据对应的地址,这样既可以快速找到数据,也解决了引用类型
可能过大不好储存的问题,这里说的也单单是我个人的简单理解,接下来结合例子可能更清晰些:
浅拷贝
首先来看看这个:
let obj1 = {
name: 'airhua',
age: '20',
score: {
math: [90, 85, 86],
english: [70, 85, 80]
}
}
let obj2 = obj1
obj2.score.math[1] = 99
console.log(obj2);
console.log(obj1);
来看看结果:
可以看到随着obj2中数值改变,obj1值也改变了,这便产生了浅拷贝。
实际在上述过程中将obj1赋值给obj2是把obj1指向的地址拷贝给了obj2,所以两个对象指向了同一个地址,一个对象修改值另一个对象也跟随改变。
深拷贝
既可以克隆第一级属性,如果某个属性又是一个内嵌的子对象,深克隆会进入子对象中,继续克隆内嵌子对象及其内容。
为了解决前面的问题我们则需要实现一个深拷贝函数来让新对象开辟一个新空间,这里需要考虑几个点
- 注意判断值类型和引用类型,值类型直接返回
- 注意判断是数组还是对象,数组和对象确定返回结果
- 注意用Object.hasOwnProperty()保证不是原型属性
- 递归调用
function cloneDeep(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj
}
let result
if (obj instanceof Array) result = []
else result = {}
for(key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = cloneDeep(obj[key])
}
}
return result
}
然后我们再来运行以下代码:
let obj1 = {
name: 'airhua',
age: '20',
score: {
math: [90, 85, 86],
english: [70, 85, 80]
}
}
let obj2 = cloneDeep(obj1)
obj2.score.math[1] = 99
obj2.name = 'air'
console.log(obj2);
console.log(obj1);
此时看看控制台结果为:
可以看到,obj2改变值并不影响obj1了,实际上就是在内存中指向空间不同,新开辟的空间在初始化时候把原有值拷贝上了。