美文网首页
深拷贝、浅拷贝

深拷贝、浅拷贝

作者: 我写的代码绝对没有问题 | 来源:发表于2021-07-26 14:57 被阅读0次

    业务场景
    项目中,需要对复选框的选中数据进行处理,处理成后端需要的参数格式。
    这里就需要对数据进行复制,我使用“=”直接把一个数组赋值给另一个变量。
    这种使用“=”等号的形式复制,就是数组的浅拷贝,浅拷贝会出现一个问题,就是对其中一个数组进行修改,实际会影响拷贝出来的其他数组。

    JS中对象分为基本类型引用类型,基本类型存放在栈内存,引用类型存放在堆内存。
    堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量

    下面开始讲解复制:
    这种只是简单的变量,内存小,我们直接复制不会发生引用。

    var a=123;
    var b=a;
    a=123456;
    alert(a); //123456
    alert(b); //123
    //或者是
    var a='afafas';
    var b=a;
    a='fgfdsdsgs';
    alert(a); //fgfdsdsgs
    alert(b); //afafas
    

    我的理解:
    基本类型存放在栈内存,栈内存存放的一些基本变量,比如像上面这种简单的变量,直接复制不会发生引用,复制后二者只是值一样而已,不会相互影响。

    无论是 concat, Spread syntax 还是 Object.assign 执行的都是浅拷贝,不是深拷贝,也就是只遍历一层。

    1. concat() 方法用于连接两个或多个数组。。

     var arr1 = [1, 2, 3];
     var arr2 = arr1.concat();
     arr1[0] = 4;
     console.log(arr1); //4, 2, 3
     console.log(arr2); //1, 2, 3
    

    2.slice() 方法可从已有的数组中返回选定的元素。
    arrayObject.slice(start,end)
    该方法返回一个新的数组,包含从 start 到 end (不包括该元素,数学上来讲是左闭右开,即包含左,不含右)的 arrayObject 中的元素。

     var arr1 = [1, 2, 3];
     var arr2 = arr1.slice(0);
     arr1[0] = 4;
     console.log(arr1); //4, 2, 3
     console.log(arr2); //1, 2, 3
    

    看到有人遇到这种问题:

    image.png

    首先,上图的实现都是浅拷贝,在使用concat和slice时,需要注意,如果数组中是简单类型,那么可以实现不相互影响,不发生引用,但如果数组里面含有复杂类型,对象这种,就出事了。

    let arr_1 = [1, 2, false, 'a']
    let arr_2 = [].concat(arr_1)
    
    arr_1[1] = 3
    
    // arr_1中的数据更改,并不会影响arr_2
    console.log(arr_1) // -> [1, 3, false, 'a']
    console.log(arr_2) // -> [1, 2, false, 'a']
    

    如果数组中有复杂数据类型,它就出事儿了

    let arr_1 = [1, 2, false, {a: 1}]
    let arr_2 = [].concat(arr_1)
    
    arr_1[3].a = 2
    
    // arr_1中的数据更改,arr_2中的数据会跟着变
    console.log(arr_1) // -> [1, 2, false, {a: 2}]
    console.log(arr_2) // -> [1, 2, false, {a: 2}]
    

    对于对象这种内存占用比较大的来说,直接让复制的东西等于要复制的,那么就会发生引用,因为这种复制,只是将复制出来的东西的指向指向了要复制的那个东西,简单的说,就是两个都同时指向了一个空间,如果改变其中一个,另一个也会发生变化。这就发生了引用,所以新旧数据会互相影响。

    引用只发生在对象的身上:

    var arr1=[1,2,3];
    var arr2=arr1;
    arr1.push(4);
    alert(arr1); //1234
    alert(arr2); //1234
    arr2.push(5);
    alert(arr1); //12345
    alert(arr2); //12345
    

    所以对于数据,我们可以使用ES6的两种新的复制方法,不会发生引用。
    第一种:Array.from(要复制的数组);

    var arr1=[1,2,3];
    var arr2=Array.from(arr1);
    arr1.push(4);
    alert(arr1);  //1234
    alert(arr2);  //123
    arr2.push(5);
    alert(arr1);  //1234
    alert(arr2);  //1235
    

    第二种:...

    var arr1=[1,2,3];
    var arr2=[...arr1];
    arr1.push(4);
    alert(arr1);  //1234
    alert(arr2);  //123
    arr2.push(5);
    alert(arr1);  //1234
    alert(arr2);  //1235
    

    这种方法也可以用在函数的形参上面。

    function show(...arr1){  //直接来复制arguments这个伪数组,让它变成真正的数组,从而拥有数组的方法。
      alert(arr1); //1234
      arr1.push(5);
      alert(arr1); //12345
    }
    show(1,2,3,4)
    

    第三种:JSON.stringify 和 JSON.parse并不能拷贝函数
    JSON.parse(JSON.stringify(obj));

    深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。

    • 深复制在计算机中开辟了一块内存地址用于存放复制的对象,
    • 而浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。

    所谓的浅复制,只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”。

    js分为基本数据类型和引用数据类型,基本类型有Number 、 String、Null、Boolean、Undefined,基本数据类型是按值访问的,直接等号赋值是值复制;
    Object、Array、Function、Data等属于引用数据类型,引用数据类型只能操作对象在栈内存中的引用地址,等号赋值是引用赋值

    推荐阅读,写的很好理解 https://www.cnblogs.com/echolun/p/7889848.html

    相关文章

      网友评论

          本文标题:深拷贝、浅拷贝

          本文链接:https://www.haomeiwen.com/subject/hzgwmltx.html