zoukankan      html  css  js  c++  java
  • 论基本数据类型与引用数据类型以及深浅拷贝的区别

    一、数据类型

    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

  • 相关阅读:
    视频智能分析系统EasyCVR视频流媒体安防监控云服务实现城市视频智能化应用
    Windows/Android/iOS平台H265编码视频播放器EasyPlayerRTSP中socket选项之keepalive设置介绍
    【解决方案】人脸识别/车牌识别RTSP/GB28181/SDK/Ehome协议视频平台EasyCVR搭建美丽乡村视频监控系统方案
    【操作说明】新版网络穿透+云端组网+远程运维+视频流拉转推平台EasyNTS上云网关管理平台如何安装?
    Windows/Android/iOS平台H265编码视频播放器EasyPlayerPro支持高码率视频播放和D3D画面旋转代码参考
    【操作说明】人脸识别/车牌识别系统视频智能分析平台EasyCVR如何配置开启HTTPS协议?
    程序员们 不要想一辈子靠技术混饭吃
    jsonObject的使用
    程序员们 不要想一辈子靠技术混饭吃
    从 iBatis 到 MyBatis
  • 原文地址:https://www.cnblogs.com/wxh0929/p/11133559.html
Copyright © 2011-2022 走看看