• 深拷贝与浅拷贝


      浅拷贝

      复制一层对象的属性,并不包括对象里面的为引用类型的数据,当改变拷贝的对象里面的引用类型时,源对象也会改变。

      深拷贝

      重新开辟一个内存空间,需要递归拷贝对象里的引用,直到子属性都为基本类型。两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

      javaScript的变量类型

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


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

      简述两种类型的存储方式:

       a基本类型(深拷贝)--名值存储在栈内存中,例如let a=1;

    栈内存
    a 1

           当b=a复制时,栈内存会开辟一个内存

    栈内存
    a 1
    b 1

      所以当你此时修改a=2,对b并不会造成影响。当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。

       b.引用数据类型(浅拷贝)--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值

    栈内存 堆内存
    name val val
    a

     堆地址1

    [0,1,2]

       当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。

        而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

      浅拷贝的实现

      1)简单的引用复制

      

        function shallowClone(copyObj) {
            var obj = {};
            for (var i in copyObj) {
                obj[i] = copyObj[i];
            }
            return obj;
        }
        var x = {
            a: 1,
            b: { f: { g: 1 } },
            c: [1, 2, 3]
        };
        var y = shallowClone(x);
        x.b.f.g = 6
        console.log(y.b.f.g);  // 6

      2)Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象

        let obj1 = { a: 0, b: { c: 0 } };
        let obj2 = Object.assign({}, obj1);
        console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
    
        obj2.b.c = 3;
        console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
        console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}

      深拷贝的实现

      1)Array的slice和concat方法
         Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。之所以把它放在深拷贝里,是因为它看起来像是深拷贝。而实际上它是浅拷贝。

        let a = [0, 1, [2, 3], 4],
            b = a.slice();
        a[0] = 1;
        a[2][0] = 1;
        console.log(a); // [1,1,[1,3],4]
        console.log(b); // [0,1,[1,3],4]

         从以上示例中可以看出拷贝的不彻底,b对象的一级属性确实不受影响了,但是二级属性还是没能拷贝成功,仍然脱离不了a的控制,说明slice根本不是真正的深拷贝。

         第一层的属性确实深拷贝,拥有了独立的内存,但更深的属性却仍然公用了地址,所以才会造成上面的问题。

         同理,concat方法与slice也存在这样的情况,他们都不是真正的深拷贝,这里需要注意。

      2)利用递归复制所有层级属性,实现深拷贝

        function deepClone(obj) {
            let objClone = Array.isArray(obj) ? [] : {};
            if (obj && typeof obj === "object") {
                for (key in obj) {
                    if (obj.hasOwnProperty(key)) {
                        //判断ojb子元素是否为对象,如果是,递归复制
                        if (obj[key] && typeof obj[key] === "object") {
                            objClone[key] = deepClone(obj[key]);
                        } else {
                            //如果不是,简单复制
                            objClone[key] = obj[key];
                        }
                    }
                }
            }
            return objClone;
        }
    
        let a = [1, 2, 3, 4]
        b = deepClone(a);
        a[0] = 2;
        console.log(a);// [2,2,3,4]
        console.log(b);// [1,2,3,4]

      3)jQuery.extend()方法源码实现

         $.extend( [deep ], target, object1 [, objectN ] )

         deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝

         target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

         object1 objectN可选。 Object类型 第一个以及第N个被合并的对象。

        let a = [0, 1, [2, 3], 4],
            b = $.extend(true, [], a);
        a[0] = 1;
        a[2][0] = 1;
        console.log(a)// [1,1,[1,3],4]
        console.log(b)// [0,1,[2,3],4]

      注意:jQuery的extend方法使用基本的递归思路实现了浅拷贝和深拷贝,但是这个方法也无法处理源对象内部循环引用,例如:

        var a = { "name": "aaa" };
        var b = { "name": "bbb" };
        a.child = b;
        b.parent = a;
        $.extend(true, {}, a);//直接报了栈溢出。Uncaught RangeError: Maximum call stack size exceeded

      4)JSON对象的parse和stringify
         JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。

        obj1 = { a: 0, b: { c: 0 } };
        let obj3 = JSON.parse(JSON.stringify(obj1));
        obj1.a = 4;
        obj1.b.c = 4;
        console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
        console.log(JSON.stringify(obj1)); // { a: 4, b: { c: 4}}

     

  • 相关阅读:
    在windows系统下,配置vue项目一键启动文件
    CSS中设置元素的圆角矩形
    CSS中使用文本阴影与元素阴影
    如何使用CSS3中的结构伪类选择器和伪元素选择器
    CSS中的z-index属性如何使用
    深入学习CSS中如何使用定位
    css中如何使用border属性与display属性
    深入了解CSS中盒子模型
    CSS中如果实现元素浮动和清除浮动,看这篇文章就足够了
    CSS标准文档流
  • 原文地址:https://www.cnblogs.com/Ann-web-1/p/11251599.html
Copyright © 2020-2023  润新知