深拷贝和浅拷贝

120 阅读3分钟

实现深拷贝/浅拷贝的方法:

浅拷贝: 如果A拷贝了B,当修改B时,如果A发生改变说明这是浅拷贝

深拷贝:但是修改A,B不会改变。如果修改了A但是B没有发生改变,就是深拷贝

let a = [1,2,3,4,5],
    b = a;// 浅拷贝
    console.log(a === b);
    a[0] = 9;
    console.log(a,b); //打印结果 [9,2,3,4,5], [9,2,3,4,5]

为什么浅拷贝改变被拷贝的对象,依靠拷贝的对象也会发生改变呢

这就涉及js数据类型了。

js数据类型大体分为两种:基本数据类型,引用数据类型

 基本数据类型: numberstringboolean, undeined, nullsymbolBiglnt(未来es10新增的类型,任意精度整数)
 引用数据类型: Object(有常规的键名键值对的无序对象,数组以及函数)
 

两类数据类型储存数据的方式如下:

基本类型:名和值都存在栈中
image.png

引用类型:名存在栈中,存在于堆内存中 image.png 因此如果创建变量A时变量A等于基本类型,B = A那么B也将值存在于栈内存中,此时进行更改A,B都不会互相影响,但是这不算是深拷贝,深拷贝本身只针对比较复杂的Object类型数据。

let a = 1,
    b = a;
    console.log(a,b); // 1,1
    b = 2;
    console.log(a,b); // 1,2
    a = 3;
    console.log(a,b); // 3,2

因为之前创建的A为引用类型值为堆内存地址,B拷贝A,B的值指向的时A的堆内存地址,两者指向同一个堆内存地址,所以当其中一个发生改变时,另一个也会随之发生改变。

实现深拷贝

1.可以递归去复制所有层级的属性, 可以自己封装一个深拷贝的函数

function cloneFuc (obj) {
    let objClone = obj instanceof Array ? [] : {};
    if (obj && typeof obj === 'object') {
        for ( key in obj ) {
            if (obj.hasOwnProperty(key)){
                //检测该属性是否不是对象原型上的属性,如果不是递归调用
                //如果是就不做任何操作,因为深拷贝不需要拷贝原型上的属性
                objClone[key] = cloneFuc(obj[key]);
            }
        }
    }
    return objClone;
}
let a = [1,2,3,4],
    b = cloneFuc(a);
    a[0] = 0;
    console.log(a,b); // [0,2,3,4],[1,2,3,4]
    /*深拷贝成功*/

2.利用slice对数组进行深拷贝

 let a = [1,2,3,4],
     b = a.slice();
     a[1] = 0;
     console.log(a,b); // [0,2,3,4],[1,2,3,4]
     /*深拷贝成功*/
 let a2 = [[1,2],3,4,[5,6],7],
     b2 =a2.slice();
     a2[2] = 0;
     a2[0][1] = 0;
     console.log(a2,b2); // [[1,0],0,4,[5,6],7] , [[1,0],3,4,[5,6],7];
     /*深拷贝失败*/
     /*第一层的数据确实时拷贝成功了,拥有了独立的内存,但是第二层还是公用的内存地址,所以深拷贝不彻底*/

3.利用concat进行深拷贝

let a = [1,2,3,4],
    b = a.concat();
    a[0] = 0;
    console.log(a,b); // [0,2,3,4], [1,2,3,4]
    /*深拷贝成功*/
let a2 = [1,2,3,[4,5,6]],
    b2 = a2.concat();
    a2[0] = 0;
    a2[3][0] = 0;
    console.log(a2,b2); // [0,2,3,[0,5,6]], [1,2,3,[0,5,6]]
    /*深拷贝失败*/
    /*错误原因同slice*/

4.利用JSON对象的stringify和parse

function objClone (obj) {
    let objclone = JSON.stringify(obj),
        objcloneTwo = JSON.parse(objclone);
    return objcloneTwo;
}
let a = [1,2,3,[4,5]],
    b = objClone(a);
    a[0] = 0;
    a[3][0] = 0;
    console.log(a,b); // [0,2,3,[0,5]], [1,2,3,[4,5]]
    /*深拷贝成功*/

5.jQuery提供了extend的方法。 $.extend([deep],target,objectN); deep:表示是否深拷贝,true为深拷贝,false为浅拷贝 target:目标对象,其他对象的属性拷贝的这个对象上 obejctN: 可以为数组,数组元素为被拷贝的对象

    let a = [0,1,[2,3],4],
        b = $.extend(true,[],a);
        a[0] = 1;
        a[2][0] = 0;
        console.log(a,b); // [1,1,[0,3],4],[0,1,[2,3],4]
        /*深拷贝成功*/

总而言之,实现深拷贝就是将需要被拷贝的对象的所有层级的数据复制到目标对象的对应层级。

本文是本人对深浅拷贝的学习和整理这里借用了一下资料的思想

【JS】深拷贝与浅拷贝的区别,实现深拷贝的几种方法