有三个数组a,b,c。将a和c拼成一个新数组d,将b和c拼成一个新数组e。修改了d中原来是c的值,发现e中原来c的值也发生了变化。这个问题我一看就知道是使用的浅拷贝,于是推荐使用了concat去拼接两个数组,然而发现还是同样的问题。
于是我赶紧试了下使用concat方法拼接数组并修改其中一个数组中的值,感觉没什么问题呀。。。修改了b数组中的值,c数组中没有发生变化。
然后又问了一下同事的场景,发现他在数组中的数据类型不光是基本数据类型,还有object引用类型。难道concat方法不是真正的深拷贝,没有对数组中的引用类型做深拷贝?
接下来赶紧写了一个例子试一下,发现果然是这个样子的,concat方法并不是深拷贝(吐槽下,网上很多博客都写了concat和slice方法可以实现数组的深拷贝,还有Object.assign()方法也是不能实现真正的深拷贝)。
那什么可以实现真正意义上面的深拷贝呢?
首先需要说明的是,js中基本数据类型不存在深拷贝还是浅拷贝的说法。因为在复制的过程中,都会开辟一个新的内存存放名称和值。深拷贝和浅拷贝的说法是针对引用数据类型来说的。在浅拷贝的过程中实际在新开辟的内存中存放的不是真实的值,而是指向这个值的地址。
下面是我总结的几种实现深拷贝方法:
1. 使用递归去复制所有层级中的基本数据类型属性
function cloneDeep(obj){
let clone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
if(obj[key]&&typeof obj[key] ==="object"){
clone[key] = cloneDeep(obj[key]); }else{
clone[key] = obj[key]; }
}
}
}
return objClone;
} 2. 使用JSON对象的parse和stringify方法实现。这种方式是先将引用数据类型转为字符串,再进行拷贝,然后再将字符串转换为引用数据类型。
function cloneDeep(obj){
let _obj = JSON.stringify(obj),
clone = JSON.parse(_obj);
return objClone
} 3. 使用loadsh中的_.cloneDeep。查看loadsh源码,这个实际也是封装了递归复制属性的方法
4. 使用jQuery中的extend方法
let obj=[0,1,[2,3],4],
clone=$.extend(true,[],obj); // true表示深拷贝以上就是我在想到的一些解决方法。 注意concat,slice及Object.assign()方法不是深拷贝。