深拷贝和浅拷贝

303 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

浅拷贝

赋值(=)和浅拷贝的区别

以下例说明:
var obj1 = {
    'name' : 'zhangsan',
    'age' :  '18',
    'language' : [1,[2,3],[4,5]],
};

var obj2 = obj1;


var obj3 = shallowCopy(obj1);
function shallowCopy(src) {
    var dst = {};
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {
            dst[prop] = src[prop];
        }
    }
    return dst;
}

obj2.name = "lisi";
obj3.age = "20";

obj2.language[1] = ["二","三"];
obj3.language[2] = ["四","五"];

console.log(obj1);  
//obj1 = {
//    'name' : 'lisi',
//    'age' :  '18',
//    'language' : [1,["二","三"],["四","五"]],
//};

console.log(obj2);
//obj2 = {
//    'name' : 'lisi',
//    'age' :  '18',
//    'language' : [1,["二","三"],["四","五"]],
//};

console.log(obj3);
//obj3 = {
//    'name' : 'zhangsan',
//    'age' :  '20',
//    'language' : [1,["二","三"],["四","五"]],
//};

先定义个一个原始的对象 obj1,然后使用赋值得到第二个对象 obj2,然后通过浅拷贝,将 obj1 里面的属性都赋值到 obj3 中。也就是说:

obj1:原始数据 obj2:赋值操作得到 obj3:浅拷贝得到

然后我们改变 obj2 的 name 属性和 obj3 的 name 属性,可以看到,改变赋值得到的对象 obj2 同时也会改变原始值 obj1,而改变浅拷贝得到的的 obj3 则不会改变原始对象 obj1。这就可以说明赋值得到的对象 obj2 只是将指针改变,其引用的仍然是同一个对象,而浅拷贝得到的的 obj3 则是重新创建了新对象。 然而,我们接下来来看一下改变引用类型会是什么情况呢,我又改变了赋值得到的对象 obj2 和浅拷贝得到的 obj3 中的 language 属性的第二个值和第三个值(language 是一个数组,也就是引用类型)。结果见输出,可以看出来,无论是修改赋值得到的对象 obj2 和浅拷贝得到的 obj3 都会改变原始数据。 这是因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变。

深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象,

浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象

Snipaste_2021-10-28_09-54-09.png

深拷贝

深拷贝就是对对象以及对象的所有子对象进行拷贝。
怎么进行深拷贝呢?
思路就是递归调用刚刚的浅拷贝,把所有属于对象的属性类型都遍历赋给另一个对象即可。

扩展运算符是深拷贝还是浅拷贝

以下例说明:

let arr = [1, 2, 3, 4, 5, 6]; 
let arr1 = [...arr]; 
arr1.push(7); 
console.log(arr);//[1, 2, 3, 4, 5, 6]
console.log(arr1); //[1, 2, 3, 4, 5, 6, 7]

当数组是一维数组时,扩展运算符可以进行完全深拷贝,改变拷贝后数组的值并不会影响拷贝源的值。但是,当数组为多维时:例如

let arr = [1, 2, 3, 4, 5, 6, [1, 2, 3]];
let arr1 = [...arr];
arr1.push(7);
arr1[arr1.length - 2][0] = 100;
console.log(arr); //[1, 2, 3, 4, 5, 6,[100, 2, 3]]
console.log(arr1); //[1, 2, 3, 4, 5, 6, [100, 2, 3],7]

由此可见,不难发现当改变拷贝后数组中第二层数组的值时,则拷贝前数组第二层数组的值也跟着改变了。

结论:扩展运算符,如果只是一层数组或是对象,其元素只是简单类型的元素,那么属于深拷贝(就是一层拷贝,可以理解为深拷贝吧!) 如果数组或对象中的元素是引用类型的元素,那么就是浅拷贝。

JSON.parse(JSON.stringfy(xxx))实现深拷贝 利用JSON.parse(JSON.stringfy(xxx))也可实现深拷贝。

注意:JSON.parse(JSON.stringfy(xxx))的方法,如果变量中含有Promise对象,则不可以使用该方法。