前端系统化学习【JS篇】:(四-1)基本数据类型之Number篇

570 阅读13分钟

前言

  • 细阅此文章大概需要 40分钟\color{red}{40分钟}左右
  • 本篇中详细讲述\color{red}{详细讲述}了:
    1. Number数字数据类型简述
    2. isNaN(检测一个值是否为非有效数字)方法
    3. 自增及其特殊性
    4. 其他类型值转换为数字类型的方法和各种情况
    5. 数组对象以及map方法回调函数的类型转换例子
    6. 进制转换
  • 如果有任何问题都可以留言给我,我看到了就会回复,如果我解决不了也可以一起探讨、学习。如果认为有任何错误都还请您不吝赐教,帮我指正,在下万分感谢。希望今后能和大家共同学习、进步。
  • 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
  • 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!

Number数字数据类型

包含:常规数字、NaN、Infinity

Infinity

  • 无穷大值

NaN

  • NaN(NOT A NUMBER):不是一个数,但是隶属于数字类型
  • NaN和任何值都不相等,甚至和自己都不相等; NaN!==NaN\color{red}{NaN!==NaN} + 【但是用Object.is(NaN,NaN)判断两个值是否相等,结果是true】
  • 所以我们不能用相等的方式判断是否为有效数字,需要使用isNaN方法来判断
    NaN==NaN
    //false
    NaN===NaN
    //false

isNaN(检测一个值是否为非有效数字)

  • 检测一个值是否是非有效数字
  • 如果【不是有效数字】则返回true
  • 反之【是有效数字】则返回false
  • [val]:参数描述占位符
  • 在使用isNaN进行检测的时候,其机制将首先会验证检测的值是否为数字类型,如果不是,先基于Number()这个方法,把值转换为数字类型,然后再检测。
  • 如isNaN('10'); //为false,因为在检测前由于此机制,自动转型了
  • 凡是能够转成数字之后是有效数字的,都是有效数字\color{red}{凡是能够转成数字之后是有效数字的,都是有效数字}
           //isNaN([val])
           console.log(isNaN(10));//false
           console.log(isNaN('AA'));//True
           /* 
               实际上在检测之前相当于发生了:
               Number('AA');  =>NaN 【转不成数字】
               isNaN(NaN);  =>TRUE
           */
     		//
           console.log(isNaN('10'));//false
           /* 
           	  类型转换机制
               实际上在检测之前相当于发生了:
               Number('10');  =>10
               isNaN(10);  =>false
           */
    

自增的特殊性

  • 由于+号的特殊性质 【可代表数学运算】,【也可代表字符串拼接】 ,所以在i++; i+=n; i=i+n; 这三种运算形式中也有些微的差别。
  • i++;和其他两种不完全一样,其代表着纯粹的数学运算
    let i = '10';
    i=i+1;//=>'10'+1=>'101'【结果为字符串拼接】
    i+=1;//'10'+1=>'101'【结果为字符串拼接】
    i++;//=>10=>10+1=>11【结果为数字11,过程为数学运算】

把其他类型值转换为数字类型

  • 两类方法:手动转换和隐式转换

手动转换:

  • Number([val])\color{red}{Number([val])} 把所有有效数字字符都进行转换 (前导的0会被忽略)
  • 【用Number把引用数据类型转换为数字】
    • 先把它基于toString方法转换为字符串,再基于Number方法转换为数字。【这条规则是浏览器或者叫JS底层的渲染规则】
  • 用Number转换走的就是这条底层规则

  • parseInt/parseFloat([val],[进制])\color{red}{parseInt/parseFloat([val],[进制])} 也是转换为数字的方法,规则不同于Number()。
  • 对于字符串来说,是从左到右依次查找有效数字字符,直到遇到非有效数字字符,停止查找【不管后面是否还有数字,都再不找了】,把找到的当作数字返回【若非字符串,同样先转换为字符串】
  • parseInt/parseFloat走的是一个额外单独方法的规则,那就是如果不是字符串,就先变成字符串,再从左到右查找(不是字符串无法进行从左到右的查找)

隐式转换:【浏览器内部默认要先转换为Number再进行计算的】

  1. isNaN([val])
  2. ==数学运算(特殊情况:+在出现字符串或对象的情况下不是数学运算,而是字符串拼接)【只有前++/++后/+i的时候也可能是数学运算】
  3. ==进行比较的时候 一些情况下会将数据类型进行转换为数字

1.Number([val])的使用场景

  • ==1== 把字符串转换为数字
  • 只要字符串中包含 任何一个非有效数字字符(第一个点除外,其作为小数点存在),结果都会是NaN
  • 空字符串'',"",会转换为0。
 //============Number=================
 //字符串转换为数字
 console.log(Number(12.5));//12.5
 console.log(Number('12.5'));//12.5
 console.log(Number('12.5px'));//NaN
 console.log(Number('12.5.5'));//NaN
 console.log(Number(''));//0

  • ==2== 把布尔值转换为数字
  • true转换为1
  • false转换为0
 // 把布尔值转换为数字
 console.log(Number(true));//1
 console.log(Number(false));//0
 //练习
 console.log(isNaN(false));//Number(false) =>0(是有效数字) =>false
 console.log(isNaN(true));//Number(true) =>1(是有效数字) =>false

  • ==3== NULL和undefined转换为数字
  • null会转换为0
    • 【null代表空对象指针,虽然代表没有,但是是有一个代表"空"的对象存在的,所以转换为Number应为0】
  • undefined转换为NaN
    • 【undefined代表未定义,未定义代表没有赋值,没有赋值就是"没有值","没有值"转换为Number的值就是NaN】
  // 把null和undefined转换为数字
  console.log(Number(null));//=>0
  console.log(Number(undefined));//=>NaN
  console.log(isNaN(null));//Number(null) =>0(是有效数字) =>false
  console.log(isNaN(undefined));//Number(undefined) =>NaN(不是有效数字) =>true

  • ==4== 引用数据类型【对象或函数】转换为数字
  • 普通对象、正则表达式对象、日期对象....转换为数字都是NaN
  • 只有数组对象有可能转换为数字【数组也是对象类型的!!!】
    • 【空数组转换为0】
    • 【只有一项的值为'能变为有效数字的值'的数组转换为该'能变为有效数字的值'的最终数值】
    • 转换过程:把引用数据类型转换为数字,先把它基于toString方法转换为字符串,再基于Number方法转换为数字【实际上是先基于valueOf获得原始值,若没有原始值再去toString】
  • 【关键点:是分为两步的!!!,先toString转为字符串,再Number转为数字】

  1. 普通对象等转换为数字
             //普通对象等转换为数字
             console.log(Number({name:'10'}));//=>NaN
             console.log(Number({}));//=>NaN
             //{}/{xxx:'xxx'}.toString()=>"[object object]"=>NaN
             /* 
                    把普通对象转换为数字,实际上发生了
                    ({name:'10'}).toString() =>"[object object]"
                    Number("[object object]") =>NaN
                    ({}).toString() =>"[object object]"
                    Number("[object object]") =>NaN
             */
                console.log(10+{});
                //=>【对象转数字先变为字符串,然后开始拼接】
                //=> "10[object object]"
    

  1. 数组对象转换为数字
          //把数组对象转化为数字
          console.log(Number([]));//=>0
          console.log(Number([12]));//=>12
          console.log(Number([12,13]));//=>NaN
          /* 
              把数组对象转换为数字,实际上发生了
              [].toString() =>""//空字符串
              Number("") =>0
      		//
              [12].toString() =>"12"//字符串"12"
              Number("12") =>12
      		//
              [12,13].toString() =>"12,13"//字符串"12,13"
              Number("12,13") =>NaN//字符串中含有非有效数字
      		//
          */
    

2.parseInt/parseFloat([val],[进制])的使用场景

  • 【注意:parseFloat不支持第二个参数!!!】
  • 【注意:parseInt可以识别各种整数格式,如:8进制,10进制,16进制,所以可以接收第二个参数表示要转换为多少进制】
    	[1,2,3].map(parseInt)
    	//[1, NaN, NaN]
            //因为parseInt接受的是两个参数,所以 ['1','2','3'].map(parseInt) 
    	//就是将字符串1,2,3作为元素;0,1,2作为下标分别调用 parseInt 函数。
            //即分别求出 parseInt('1',0), parseInt('2',1), parseInt('3',2)的结果。
            //后面两个作为进制,前面传来的数值自然是超出了范围,返回NaN
    

  • parseInt/parseFloat走的是一个额外单独方法的规则,那就是如果不是字符串,就先变成字符串,再从左到右查找(不是字符串无法进行从左到右的查找)遇到非有效数字字符就不再继续查找了,没找到则NaN
        //用parseInt/parseFloat将各个数据类型转换为数字
        console.log('============');
        let str = '12.5px';
        let str1 = 'width:12.5px';
        console.log(Number(str));//=>NaN
        /* 返回有效数字字符 */
        console.log(parseInt(str));//=>12
        console.log(parseFloat(str1));//=>NaN
        console.log(parseInt(''));//=>NaN(字符串从左到右找,没有找)
        parseFloat("1.6px")+parseInt("1.2px")+typeof parseInt("null")//=>"2.6number"
        /* 
            parseInt
            从左到右依次查找有效数字字符,且只找Int类型的整数
            遇到非有效数字字符就不再继续查找了【无论后面是否还有数字】,把找到的整型数字当作数字返回,没找到则NaN
        */
        console.log(parseFloat(str));//=>12.5
        /* 
            parseFloat
            从左到右依次查找有效数字字符,且只找Float类型的浮点型数字
            遇到非有效数字字符就不再继续查找了【无论后面是否还有数字】,把找到的浮点型数字当作数字返回,没找到则NaN
        */
        console.log(parseInt(null));//=>NaN
        console.log(parseInt(undefined));//=>NaN
        console.log(parseInt(false));//=>NaN
        console.log(parseInt(true));//=>NaN
        /* 
            parseInt/parseFloat
            如果是boolean,null,undefined类型值,则都会返回NaN
        */
        console.log(parseInt({}));//=>NaN
        /* 
            parseInt/parseFloat
            如果是普通对象,则都会返回NaN
            ({}).toString() =>"[object object]"
            parseInt/parseFloat("[object object]") =>NaN
        */
        console.log(parseInt([]));//=>NaN
        /* 
            parseInt/parseFloat
            如果是空数组对象,则会返回NaN【因为里面没有有效数字字符】
            [].toString() =>""//空字符串
            parseInt/parseFloat("") =>NaN
        */
        console.log(parseInt([12]));//=>12
        console.log(parseFloat([12.5]));//=>12.5
        console.log(parseInt([12,13]));//=>12
        /* 
            parseInt/parseFloat
            如果是非空数组对象,则会按照字符串的原则进行转换
        */
    
  • 其他可参考文章

3.数学运算

  • 字符串: (特殊情况:+在出现字符串或对象的情况下不是数学运算,而是字符串拼接)【只有前++/++后/+i的时候也可能是数学运算】
        let a  = "10";
        ++a;//=>11
    
    1. 【对象+数字:不一定都是字符串拼接】 【重要】\color{red}{【重要】}
      • 对象本身是想转换为数字再进行运算的,而【对象转换为数字】并不是先toString直接转换为字符串
      • 而是先.valueOf获取原始值[[PrimitiveValue]],
      • 如果有原始值(原始值都是基本数据类型值),直接基于原始值处理】
      • 没有原始值才去toString
    2. 【基本数据类型和引用数据类型调用valueof的区别】【重要】\color{red}{【重要】}
      • 【基本数据类型值所对应的【类】的原型】上都有【valueOf】这个属性
      • 而【引用数据类型值所对应的【类】的原型】上大部分都没有【valueOf】这个属性,如果调用【valueOf方法】调用的都是Object原型上的该方法,而返回的也基本都是调用方法的实例本身。
    3. 【valueof唯一例外的引用数据类型Date】【重要】\color{red}{【重要】}
      • 但是在【引用数据类型中】Date日期对象的所属类的原型上是有valueOf这个方法的 。返回的是当前日期到19700101的毫秒差,但并不能算是原始值,而是其自定义的返回值
    4. 【基本数据类型和引用数据类型的原始值的区别】【重要】\color{red}{【重要】}
      • 【原始值[[PrimitiveValue]]】而基本上只有基本数据类型的值才具有【原始值】
      • 而数组、对象、正则、函数等引用类型值没有【原始值】, 通过Object.valueOf方法获取的只是该引用类型值本身。
        Number.prototype.plus = function plus(num){
        //This=>永远都是对象数据类型值【除null和undefined外】
        //【对象+数字】:不一定都是字符串拼接
        //对象本身是想转换为数字再进行运算的,而【对象转换为数字】并不是先toString直接转换为字符串,
        //而是先.valueOf获取原始值[[PrimitiveValue]],
        //【如果有原始值(原始值都是基本数据类型值),直接基于原始值处理】,
        //【没有原始值才去toString】
        return this + num;
        //通过实例调用时,直接将this代表的Number类的实例的原始值与参数进行运算,
        //而不是直接toString转为字符串【因为this永远是一个对象类型的值】
        //是先对this进行valueof取原始值,若为基本数据类型值,则会被由于this是对象类型被转为“包装类(前面说过的类似包装类的机制)【new Number(10)】”从而取到原始值【基本数据类型实例都有原始值属性】
        //引用数据类型取不到才进行toString转换成字符串,在进行字符串拼接,
    };
        Number.prototype.minus = function minus(num1){
        return this - num1;
        };
        //
        //在所属内置类的原型上实现plus和minus方法,实现要求的效果
        let n = 10;
        let m = n.plus(10).minus(5);
        console.log(m);//=>15(10+10-5)
    
  • 对象: 如果出现对象也会变为字符串拼接,因为原本应该把对象转化为数字,但转换过程中需要先转换为字符串,则+遇到字符串直接变为字符串拼接。
         console.log(10+undefined;);
         //=>10+NaN
         //=>NaN(此时仍是数学运算,没有变成字符串)
         //Number(undefined)=>NaN
    

4.==进行比较时

  • 可能出现要把其他类型值转换为数字的情况
        console.log('10' == 10;);
    
  • 在==比较的过程中,数据转换的规则
  • 【类型一样时的几个特殊点】

  • 对象比较的是堆内存的地址
    • {}=={}:false
    • []==[]:false
    • NaN和自己比较的情况
      • NaN==NaN:false
  • 【类型不一样的转换规则】

    1. null==undefined:true,但是转换成 ===【null===undefined:true全等于】结果就是false (因为类型不一致),剩下null/undefined和其他任何数据类型值都不相等
    2. 字符串==对象,会把对象转换为字符串
    3. 除了这两种情况外,如果==两边数据类型不一致,都是需要转换为数字再进行比较
      //==练习题
      console.log([]==false);//true
      //[]->""->0
      //false->0 
      console.log(![]==false)//false
      //[]->""->0  !0->1
      //false->0 
      //对象==布尔 不是类型相同也不是字符串==对象,于是都先转为数字(隐式转换),再进行比较

特殊例子

        {}+0;
        //左边的{}会被认为是一个代码块,不会参与运算
        //运算则只会处理+0 =>0
        ({}+0);
        //在数学运算后得到字符串"[Object Object]0"
        0+{};
        //在数学运算后得到字符串"0[Object Object]"

关于数组对象以及map方法回调函数的类型转换例子

  • array当中的map方法: 会循环遍历数组当中的每一项,而每循环一项就会触发调用一次回调函数,每一次还会将 【数组中当前元素】【当前元素的索引】 作为参数传递到回调函数当中。
    • map方法的返回值是一个新数组,不会修改原有数组。
  • parseInt([value],[radix]):有两个参数
    • [radix]这个值代表一个进制,不写或者写0,默认按照10进制处理【特殊情况:若[value]以0x开头,则默认值是以16计算。】
    • [radix]的取值范围是2~36,若不在此区间内,则运行结果为NaN
    • 【parseInt([value],[radix])】的意义:, 将[value]看作是[radix]进制,然后按照[radix]进制将[value]转化为10进制
    • parse方法是按照进制的规则,从左向右查找,若遇到不符合规则的,就不再向后查找。
  • parseFloat([value]):有1个参数parseFloat不支持第二个参数
    let arr = [10.18,0,10,25,23];
    arr = arr.map(parseInt);//此处的parseInt是作为回调函数执行
    console.log(arr);//[10,NaN,2,2,11]
    //分解如下
    parseInt('10.18',0);//将10
    //本来就是10进制不用转,=>10
    parseInt('0',1);
    //进制不在取值范围内,=>NaN
    parseInt('10',2);
    //10(二进制)=>1*2^1 =>2
    parseInt('25',3);
    //符合三进制的值是0 1 2,parseInt方法是按照进制的规则,从左向右查找,若遇到不符合规则的,就不再向后查找。
    //25(三进制) => 2*3^0 (5超出了三进制范围,不再查找)  =>2【注意,此时只有2一位,所以位权值是0】
    parseInt('23',4);
    //23(四进制)=> 0 1 2 3=> 2*4^1 + 3*4^0 = 11

【进制转换】把一个值转换为10进制

  • 位权值:每一位的权重,个位是0,十位是1.....
    //如147(八进制)=>十进制
    //1*8^2 + 4*8^1 + 7*8^0 = 64+32+7 = 103
    //12.23(四进制) =>十进制
    //1*4^1 + 2*4^0 + 2*4^-1 +3*4^-2 = 
    //负次幂,^-1 :1/4【取倒数】,^-2:1/(4^2)【取倒数】【相当于把相应的正数幂取倒数】