论基本数据类型与引用数据类型以及深浅拷贝的区别

一、数据类型

  1. Js有两种数据类型
    • 基本数据类型:Number、String、Boolean、Null、undefined、Symbol(ES6)等;
    • 引用数据类型:Object(数据、数组、函数、正则表达式等,除基本数据类型以外都是对象)
    • 基本数据类型与引用数据类型的区别:

         2. 栈(stack)与 堆(heap)的概念

    • 栈:自动分配的内存空间,由系统自动释放;
    • 堆:动态分配的内存空间,内存大小不一样,大小也不一定会自动释放;

    3. 这两类数据类型的存储方式

    • 基本数据类型:
      var a = 1;
      var b = a;
      a = 2;
      console.log(a,b)

       输出结果:

                   

      变a的同时b没有发生改变,因此接下来分析一下产生这种变化的原因:

        • 基本数据类型-----名与值存放在栈内存中,例如:let  a = 1;

                                       

                                          当 b = a 时,栈会新开辟一个内存,如下:

                                     

                                         所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了。

                                         当然,let a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。

    • 引用数据类型:
      • 浅拷贝
         var  a = [0,1,2,3];
         var  b = a;
         a[0] = 22;
         console.log(a,b)

         输出结果:

             

                              变a的同时b也发生了改变,因此接下来分析一下产生这种变化的原因:

        • 引用数据类型-----名存放在栈内存中,值存放在堆内存中,但是栈内存会提供一个 “引用的地址”(即指针) 指向堆内存当中的值,对上述的 “浅拷贝” 进行解析:(复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)

                             

                                        当b拷贝a时,即 b = a ,复制的是a的引用地址,而不是堆内存里面的值;

                               

                                        而当我们 a[0] = 22 进行数组修改时,由于a与b指向的是同一个地址,因此b也受了影响,这就是所谓的 “浅拷贝”;

                                  

                                       但是当我们在堆内存中也去开辟一个新的内存,用来专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了;

                                

           4、“深拷贝”的实现方式

    • 深拷贝作用:深拷贝在实际开发中是非常有用的:例如后台返回了一堆数据,你需要对这堆数据做操作,但多人开发情况下,你是没办法明确这堆数据是否有其它功能也需要使用,直接修改可能会造成隐性问题,深拷贝能帮你更安全安心的去操作数据。
    • 用递归去实现 “深拷贝”:
      • 封装一个 “深拷贝” 函数,用递归去复制所有的层级属性:
        // 一、递归
            function deepClone(obj) {
                let objClone = Array.isArray(obj) ? [] : {};
                if (obj && typeof obj === "object") {
                    for (var key in obj) {//key是属性
                        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] = 22;
            console.log(a, b);

        输出结果为:

                               

    • 通过JSON对象实现 “深拷贝” :

      var  objClone = JSON.parse(JSON.stringify( obj ));

      缺点:丢失constructor,RegExp无法实现

    • 通过jQuery的extend方法实现 “深拷贝”:
      // $.extend( [deep ], target Object, [ 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, b);

                     输出结果为:

      

    • 通过Object.assign()与 slice() 来实现 “深拷贝”(根本不是真正的深拷贝):concat()等函数也是
      • 当对象中只有一级属性,没有二级属性的时候,Object.assign()方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
        //进行拷贝 
        let obj1 = { a: 0, b: { c: 0 } };
        let obj2 = Object.assign({}, obj1);//Object.assign(target,sources)target指目标对象,sources指多个源对象
        console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
        // 当改变值时 obj1.b.c = 2; console.log(JSON.stringify(obj2))

                                输出结果为:

                                

      • 当数组只是一维数组时,可以通过 slice() 方法实现深拷贝,但是存在二维数组时,二维数组中就是浅拷贝;
        • 一维数组:
          let arr = [0,1,2,3,4];
          b = arr.slice();
          console.log("复制后的b:" + b);
          arr[0] = 22;
          console.log("当值改变时的arr:" + arr)  
          console.log("值变后b的值:" + b)  

          输出结果为:

                                         

        • 二维数组:
          let arr = [0,1,2,3,4,[5,6]];
          b = arr.slice();
          console.log("复制后的b:" + b);
          arr[0] = 22;
          arr[5][0] = 55;
          console.log("当值改变时的arr:" + arr)  
          console.log("值变后b的值:" + b)  

          输出结果:

                                       

    • lodash函数库实现 “深拷贝”:

      lodash是一个很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝,但是一旦拷贝一些很复杂的对象就有可能报错。比如用cloneDeep克隆一个vue实例,就有可能报key.charAt is not a Function的错。(正确的拷贝方法是Vue.extend())。
二、参考地址

https://www.cnblogs.com/c2016c/articles/9328725.html

https://www.cnblogs.com/echolun/p/7889848.html

原文地址:https://www.cnblogs.com/wxh0929/p/11133559.html