美文网首页IT修真院-前端
如何实现数组的浅拷贝和深拷贝

如何实现数组的浅拷贝和深拷贝

作者: xiaoyudesu | 来源:发表于2017-08-22 04:59 被阅读0次

    1.背景介绍

    在JavaScript中,对于Object和Array这类引用类型值,当从一个变量向另一个变量复制引用类型值时,这个值的副本其实是一个指针,两个变量指向同一个堆对象,改变其中一个变量,另一个也会受到影响。
    这种拷贝分为两种情况:拷贝引用和拷贝实例,也就是我们说的浅拷贝和深拷贝

     

    2.知识剖析

    基本类型:

    5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。

    引用类型:

    存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

    JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2
      指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。

    JS数组的浅拷贝
    简单的赋值就是浅拷贝。因为对象和数组在赋值的时候都是引用传递。赋值的时候只是传递一个指针。

    var a = [1,2,3];
    var b =a ;
    console.log(a,b);
    b[0]="a";
    console.log(a);
    console.log(b);
    

    浅拷贝很容易,但是很多时候我们需要原样的把数组或者对象复制一份,在修改值的时候,不改变初始对象的值。这个时候就需要使用深拷贝。

    JS数组的深拷贝

    方法一:js的slice函数

    slice() 方法可从已有的数组中返回选定的元素。
    【语法】arrayObject.slice(start,end)
    【参数】arrayObj--必选项:一个Array对象。start--必选项:arrayObj中所指定的部分的开始元素是从零开始计算的下标。end--可选项:arrayObj中所指定的部分的结束元素是从零开始计算的下标。
    【说明】
    slice 方法返回一个Array对象,其中包含了arrayObj的指定部分。slice方法一直复制到end所指定的元素,但是不包括该元素。如果start为负,将它作为length +
    start处理,此处length为数组的长度。如果end为负,就将它作为length + end处理,此处length为数组的长度。如果省略end ,那么slice方法将一直复制到 arrayObj
    的结尾。如果end出现在start之前,不复制任何元素到新数组中。

    实例:
    var a = [1,2,3,4,5];
    var b = a.slice(0,2);
    var c = a.slice(-3,-1);
    var d = a.slice(-1,-3);
    console.log(b,c,d);
    

    方法二:js的concat函数

    concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
    【语法】arrayObject.concat(arrayX,arrayy,......,arrayN)
    【参数】arrayX--必需:该参数可以是具体的值,也可以是数组对象。可以是空值,可以是任意多个。
    【说明】
    返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat()操作的参数是数组,那么添加的是数组中的元素,而不是数组。

    实例:
    var a = [1,2,3];
    var b = a.concat(4,5);
    console.log(b);
    var c = [6,7];
    var d = a.concat(c);
    console.log(d);
    

     

    3.常见问题

    除了上述两个方法外,还有没有其他的深拷贝方法?

     

    4.解决方案

    js遍历数组的方法:

    var arr1=[1,2,3,4,5],arr2=[];
    arr1.forEach(function(val,i){
    arr2[i]=val;
    })
    console.log(arr1,arr2);
    arr2[0]="a";
    console.log(arr1,arr2);
    
    利用JSON格式
    var a=[[1,2,3],4,5,6];
    var b=JSON.parse(JSON.stringify(a));
    console.log(a,b);
    a[0][0]="a";
    console.log(a,b);
    

    这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。

     

    5.编码实战

     

    6.扩展思考

    slice()、concat()的局限性在哪里?

    var arr1=[[1,2,3],4,5,6];
    var arr2=arr1.slice();
    console.log(arr1,arr2);
    arr2[0][0]="a";
    console.log(arr1,arr2);
    
    var arr1=[[1,2,3],4,5,6];
    var arr2=arr1.concat();
    console.log(arr1,arr2);
    arr2[0][0]="a";
    console.log(arr1,arr2);
    

    由上面的例子可以看出,slice和concat这两个方法,仅适用于对不包含引用对象的一维数组的深拷贝

     

    7.参考文献

    参考1:JavaScript中的浅拷贝和深拷贝
    参考2:javaScript中浅拷贝和深拷贝的实现

    8.更多讨论

    还有什么方法实现多维数组的深拷贝?

    咱们可以对刚才的数组循环改写一下:

        function deepCopy(arr1,arr2){
            arr1.forEach(function(val,i){
                if(val instanceof  Array){
                    arr2[i]=[];
                    val.forEach(function(val2,j){
                        if(val2 instanceof  Array){
                            deepCopy(arr1[j],arr2[j]);
                        }else{
                            arr2[i][j]=val2
                        }
                    })
                }else{
                    arr2[i]=val;
                }
            })
        }
    
        var a=[[[1],2,3],4,5,6],b=[];
        deepCopy(a,b);
        console.log(a,b);
        b[0][0][0]="a";
        console.log(a,b);
    

    相关文章

      网友评论

        本文标题:如何实现数组的浅拷贝和深拷贝

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