• js数组和对象相等判断、拷贝详解(结合几个现象讲解引用数据类型的趣事)


    序言

      最近遇到几个js引用数据类型造成的bug,今天结合bug详细分析一下,避免以后再犯,也希望能帮大家提个醒,强化js基本功。

    目录

      1、浅拷贝、深拷贝,解决变量赋值相互影响问题 

      2、判断2个数组、对象是否相等

    现象一

    var a=1;
    var b=a;
    b=2;
    console.log(a)      //1
    console.log(b)      //2
    
    
    var obj1 = {
          id: 1,
          info: {
                name: '张三'
          }
        };
    var obj2 = obj1; obj2.id = 2; obj2.info.name = '李四' console.log(obj1)   // {id:2, info:{name: '李四'}}
    console.log(obj2)         // {id:2, info:{name: '李四'}}

      1. 现象分析: 

        数组、对象都是用用数据类型。直接 = 赋值,或者 浅拷贝,这些变量实质上共享一个存储空间的数据。当你修改其中一个对象变量时,
      另一个也会变。 

    2. 深浅拷贝
         浅拷贝: 简单说,就是新的变量与原变量公用一个指针,即公用同一份存储空间的数据。改变a,b也会变
       深拷贝: 简单说就是把数据全部拔下来,重新存一下。a,b之间没有任何牵连

       浅析:
      1. 这涉及到计算机原理的一些东西。js基本数据类型存在 栈内存 中, 当 a=b ,相当于新开辟一个存储空间,所以互不影响。
      2. 引用数据类型,名 存在栈内存中,值存在堆内存中,栈内存会额外存一个指向堆内存的指针,其实就是 堆内存地址。
      3. 我们对象 a=b 时,仅仅将名,即 栈内存中的数据复制了出来,但是栈内存的地址并没有变,所以a,b值会保持一致,互相影响。


    3. 几个实现对象拷贝的方法
      let obj1 = { name: 11, age: 11, ope: { say: 1, get: 2 }}
      let obj2 = { }
      (1) let obj2 = { ...obj1 } let obj2 = { ...obj1 ,ope:22}
      这个拷贝不彻底,他可以深拷贝基础数据类型,name 等修改不会影响,但是数组、对象不行,ope里面的数据依旧相互影响,但是我们
      可以将这些引用数据类型给修改掉,这样依旧可以实现互不影响

      (2) let obj2 = Object.assign({ },obj1,...)
      原理是一样的,也是只对基本数据类型管用,这个灵活度,还不如上面那个

      (3) 通过 json 字符串、对象转换的方式
      obj2 = JSON.stringify(obj1) 先转 字符串
      obj2 = JSON.parse(obj2) 再转对象

    而且转成字符串以后,还方便 网络传输 、 存储。
      


    现象二
         var arr1 = [1,2,3];
            var arr2 = [1,2,3];
            var obj1 = {id:1,name:"张三"};
            var obj1 = {id:'1',name:"张三"};
            var arr3 = arr1;
            var obj3 = obj1;
    
            console.log(arr1 === arr2);//false
            console.log(obj1 === obj2);//false
            console.log(arr3 === arr1);//true
            console.log(obj3 === obj1);//true

      

      1. 现象分析:       

        因为 数组、对象是引用数据类型,变量存储的是地址,真正是数据在地址指针指向的存储单元,所以arr1、arr2的数据是放在不同存储单元里的。直接比较,地址肯定是不同的。而赋值操作 arr3 = arr1 是将地址指针赋值,数组数据还是只放在一个存储单元里面,arr1、arr2 都存储了这个地址,所以直接比较是相等的

    2. 如何实现 数组 对象的相等判断
      方法一   转换为字符串再比较
      JSON.stringify(a1) == JSON.stringify(a2)
      或
      a1.toString() == a2.toString()

      问题:存在 1 和 '1' ,认为是相同的情况,只能根据自己情况来了
      方法二
    function equalJudgment(data1,data2) {
                  var result = true
                  if (data1 && data2 && data1.constructor === Array && data2.constructor === Array) {
                    if (data1.length !== data2.length) {
                      result = false
                    } else {
                      for (var i=0;i<data1.length;i++) {
                        if (data1[i].constructor === Array && data2[i].constructor === Array){
                          if (!equalJudgment(data1[i], data2[i])) {
                            result = false
                            return result
                          }
                        } else if (data1[i].constructor === Object && data2[i].constructor === Object){
                          if (!equalJudgment(data1[i], data2[i])) {
                            result = false
                            return result
                          }
                        } else {
                          if (data1[i] !== data2[i]){
                            result = false
                            return result
                          }
                        }
                      }
                    }
                  } else if (data1 && data2 && data1.constructor === Object && data2.constructor === Object) {
                    var key1 = Object.getOwnPropertyNames(data1)
                    var key2 = Object.getOwnPropertyNames(data2)
                    if (key1.length !== key2.length) {
                      result = false
                    } else {
                      for (var i=0;i<key1.length;i++) {
                        if (key2.indexOf(key1[i]) === -1) {
                          result = false
                          return result
                        } else if (data1[key1[i]].constructor === Array && data2[key1[i]].constructor === Array) {
                          if (!equalJudgment(data1[key1[i]], data2[key2[i]])) {
                            result = false
                            return result
                          }
                        } else if (data1[key1[i]].constructor === Object && data2[key1[i]].constructor === Object) {
                          if (!equalJudgment(data1[key1[i]], data2[key1[i]])) {
                            result = false
                            return result
                          }
                        }else {
                          if (data1[key1[i]] !== data2[key1[i]]){
                            result = false
                            return result
                          }
                        }
                      }
                    }
                  } else {
                    result = 'Input error!'
                  }
                  return result
                }

      

      以上 js 函数是我自己写的,经过测试没有问题 ,网上有很多实现的方法,但是大多都是无法解析多层数组、对象嵌套的情况 (说实话没啥卵用),但是个人觉得这个方法写的有些啰嗦,抛砖引玉,有更好的方法希望大家不吝分享。   ps:顺便帮我测测bug,虽然各种情况都试过,应该是没问题

    总结

      上述的2个现象在码代码过程中应该会经常遇到,特别是处理像  react、Vue 生命周期函数的时候、state使用修改的时候,经常会遇到引用数据类型带来的问题,不经意间就出问题了,希望这些能帮到大家,若有错误,望指出!!!

  • 相关阅读:
    Scala编程基础
    大数据学习环境搭建(CentOS6.9+Hadoop2.7.3+Hive1.2.1+Hbase1.3.1+Spark2.1.1)
    hadoop2.7.3编译,支持snappy、bzip2本地压缩
    ABAP非Unicode系统中字符串拼接(CONCATENATE)时吃字符问题
    Hadoop学习笔记
    HIVE开发总结
    中文字符截取乱码问题
    替换空字符NULL(字符编码为0的字符)
    Generate Time Data(普通日期主数据)
    Generate Time Data(财务日期主数据)
  • 原文地址:https://www.cnblogs.com/pengfei-nie/p/9767156.html