js深拷贝和浅拷贝

216 阅读2分钟
昨天同事有一个问题找到了我,他的问题场景是这样的:

有三个数组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()方法不是深拷贝。