数据存储规则
数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。
对于基本数据类型来说,它声明的名字和值都存储在栈内存中。
例如 let a = 2
对于引用数据类型来说,栈内存存储的是变量名以及对象在堆内存中的存储地址,通过这个地址找到存储在堆内存的值。
深浅拷贝
对基本类型进行拷贝时,
let a = 2
let b = a
当你把 a 赋值给 b 时,会在栈内存中重新开辟一块空间来存放,所以即使你修改 a,b 的值也不会改变。
对引用数据类型进行拷贝时,
let a = [1,2,3]
let b = a
这里复制的其实是 a 的引用地址,而并非堆里面的值。当我们令 a[0] = 2 时,由于 a 与 b 都指向了同一个地址,那 b 也同时受到了影响。这就是所谓的浅拷贝。
所以简言之,如果 b 复制了 a ,如果改变了a的值,b的值也跟着改变的话,那就是浅拷贝,如果b 不受影响的话,就是深拷贝。(这里需要注意深拷贝本身只针对较为复杂的object类型数据,所以上面例子中基本类型的拷贝算不上是深拷贝)
现在我们就明白了如果能在堆内存中也开辟一个新的内存专门来存放b的值,就像基本类型那样,就达到了深拷贝的效果。
实现深拷贝的方法
可以通过 JSON.parse(JSON.stringify(object)) 来解决
let a = {
age: 1,
sex: {
first: 'man'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'woman'
console.log(b.jobs.first) // man
利用递归来实现深复制,对属性中所有引用类型的值遍历到是基本类型的值为止。
function deepClone(obj){
if(!obj && typeof obj !== 'object'){
throw new Error('error arguments', 'shallowClone');
}
var targetObj = Array.isArray(obj) ? [] : {};
for(var keys in obj){
if(obj.hasOwnProperty(keys)){
//判断ojb子元素是否为对象,如果是,递归复制
if(obj[keys] && typeof obj[keys] === 'object'){
targetObj[keys] = deepClone(obj[keys]); //递归
}else{
targetObj[keys] = obj[keys];
}
}
}
return targetObj;
}
由于我们需要考虑好多种边界情况,比如原型链如何处理、DOM 如何处理等等,所以这里我们实现的深拷贝只是很基本的。
还有一种更为推荐的实现深拷贝的方式就是使用 lodash 的深拷贝函数。
详情见https://www.jianshu.com/p/35d69cf24f1f.
本文是自己学习时的一篇笔记整理,参考了以下文章:
lhttps://www.cnblogs.com/echolun/p/7889848.html.