美文网首页
数组和对象的浅拷贝,深拷贝

数组和对象的浅拷贝,深拷贝

作者: _littleTank_ | 来源:发表于2018-06-14 10:17 被阅读0次

    在说数组和对象的浅拷贝,深拷贝的问题前我们先了解一下javaScript的变量类型。
    (1)基本类型:
    5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。

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

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

    先看下下边例子:

    var a = 10;
    var b = a;
    b = 20;
    console.log(a);//10
    console.log(b);//20
    

    上边代码在修改b的值时并不会改到a。

    但是对象和数组就不一样了,对象和数组是按引用传值,具体如下:

    //浅拷贝,双向改变,指向同一片内存空间
    var obj = {'age':20}
    var newobj = obj
    newobj.age= 30
    console.log(obj.age); //30
    console.log(newobj.age); // 30
    

    上边的代码在修改新对象newobj的age值,原来的对象obj的age值也改变了,为什么呢?因为对象和数组是按引用传值,所以他们根本是同一个对象,上面代码中,newobj 并不是obj 的克隆,而是指向同一份数据的另一个指针。修改newobj ,会直接导致obj 的变化。这就是所谓的浅拷贝。

    var obj1 = { a: 10, b: 20, c: 30 }; 
    var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
    obj2.b = 100;
    console.log(obj1); // { a: 10, b: 20, c: 30 }  这里b 沒被改到
    console.log(obj2); // { a: 10, b: 100, c: 30 }
    

    上面的代码修改拷贝的对象并不会影响原来的对象,这就是属于深拷贝的类型。
    关于浅拷贝:浅拷贝有很多种方式,但是最常用的一种就是“=”赋值类型,如下

    var obj1 = {'age':30}
    var obj2 = obj1
    

    1、数组的拷贝

    (注意下边这几种数组拷贝其实是数组的浅拷贝,但是我们可以把他当做数组的深拷贝来用,但事实是浅拷贝)

    方法1:循环遍历
    var arr1 = [1,2,3]
    var arr2 = []
    for(var i=0;i<arr1.length;i++){
      arr2[i] = arr1[i]
    }
    arr2[1]=100
    console.log(arr1 );//[1,2,3]
    console.log(arr2 );//[1,100,3]
    
    方法2:concat方法,该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
    var arr1 = [1,2,3]
    var arr2 = [].concat(arr1)
    arr2[1]=100
    console.log(arr1 );//[1,2,3]
    console.log(arr2 );//[1,100,3]
    
    方法3:es6扩展运算符
    var arr1 = [1,2,3]
    var arr2 = [...arr1]
    arr2[1]=100
    console.log(arr1);//[1,2,3]
    console.log(arr2);//[1,100,3]
    

    到这里或许你已经发现问题了,我们给的例子中的数组都是一维数组,如果换成二维数组,上边的方法还能用吗?答案是否定的,换成了多维数组后上边的方法已经不能适用。

    2、对象的拷贝

    对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

    let z = { a: 3, b: 4 };
    let n = { ...z };
    n // { a: 3, b: 4 }
    

    DeepCopy: 深拷贝

    深拷贝的实现一般是递归属性遍历

    一般来说,在JavaScript中考虑复合类型的深层复制的时候,往往就是指对于Date、Object与Array这三个复合类型的处理。我们能想到的最常用的方法就是先创建一个空的新对象,然后递归遍历旧对象,直到发现基础类型的子节点才赋予到新对象对应的位置。不过这种方法会存在一个问题,就是JavaScript中存在着神奇的原型机制,并且这个原型会在遍历的时候出现,然后原型不应该被赋予给新对象。那么在遍历的过程中,我们应该考虑使用hasOenProperty方法来过滤掉那些继承自原型链上的属性:

    function deepClone(obj) {
        var copy;
        if (null == obj || "object" != typeof obj) return obj;
        // Handle Date
        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
        // Handle Array
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = deepClone(obj[i]);
            }
            return copy;
        }
        // Handle Object
        if (obj instanceof Object) {
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]);
            }
            return copy;
        }
        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    

    我们来验证一下,如下代码

    let arr = [1,[2,4],3]
    let a = deepClone(arr)//这里deepClone函数是上边写的那个深度复制函数
    a[0]=100
    
    console.log(arr)  
    console.log(a)
    

    结果如下图


    0.png

    相关文章

      网友评论

          本文标题:数组和对象的浅拷贝,深拷贝

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