5.数值的扩展

1.二进制和八进制表示法

 ES6提供了二进制和八进制数值的新的写法们分别用前缀0b(或0B)和0o(或0O)表示。

    console.log(Number(0b111111))    //63
    console.log(Number(0o77))    //63

  从ES5开始,在严格模式中,八进制就不再允许使用前缀0表示,ES6进一步明确,要使用前缀0o表示。

1     (function(){
2         console.log(0o77 === 077)    //不报错
3     })
4     (function(){
5         'use strict';
6         console.log(0o77===077)    //Octal literals are not allowed in strict mode.
7     })

  如果要将0b和0o前缀的字符串数值转为二进制,要使用Number方法。参考第一个代码。

2.数值分隔符

  欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。如:1000可以写为1,000。

  ES2021,允许JavaScript的数值使用下划线(_)作为分隔符。

const text = 1_000_000;
console.log(text===10**6) //true

  这个数值分隔符并没有指定间隔的位数,也就是说,可以任意位数后添加一个分隔符。如:

console.log(1_0_00_000===10**6) //true
console.log(1_0_0_0_0_0_0===10**6) //true

  小数和科学计数法也可以使用数值分隔符。如:

console.log(0.0_01===0.001) //true
console.log(1e11===1e1_1)    //true

  在使用分隔符时,需要注意以下几种情况。

    -不能放在数值的最前面或者最后面

    -不能两个或两个以上的分割符连在一起

    -小数点的前后不能有分隔符

    -科学计数法里,表示指数的e或E前后不能存在分隔符

  除了十进制,其他进制也可以使用分隔符来增加可读性。

  注意,分隔符不能紧跟着进制的前缀0b、0B、0o、0O、0x、0X。否则将会报错。如

console.log(0x123) //291
console.log(0x_123)    //nvalid or unexpected token
console.log(0_x123)    //Numeric separator can not be used after leading 0

  数值分隔符的使用指数为了便于可读和书写,对于JavaScript内部数值的存储和输出,并没有影像。如:

const text = 1_23
console.log(text) //123
console.log(text.toString())    //123

  下面三个将字符串转成数值的函数,不支持数值分隔符。主要原因是语言的设计者认为,数值分隔符主要是无可编码时书写数值的方便,而不是为了处理外部输入的数据。如

    -Number()

    -parseInt()

    -parseFloat()

const text = '1_23'
console.log(Number(text))    //NaN
console.log(parseFloat(text))    //1 parseFloat如果字符串第一个是数字就会一直往下解析直到不是数字,返回。
console.log(parseInt(text))    //1    parseInt用于将字符串参数作为有符号的十进制整数进行解析

3.Number.isFinite(),Number.isNaN()

  ES6在Number对象上,新提供了Number.isFinite和Number.isNaN()两个方法。

  Number.isFinite()用来检查一个数值是否为有限的,即不是infinity。

console.log(Number.isFinite(15))    //true
console.log(Number.isFinite(0.1))    //true
console.log(Number.isFinite(NaN))    //false
console.log(Number.isFinite(Infinity))    //false
console.log(Number.isFinite(-Infinity))    //false
console.log(Number.isFinite('abc'))    //false
console.log(Number.isFinite('15'))    //false
console.log(Number.isFinite(true))    //false

  注意,如果参数类型不是数值,Number.isFinite一律返回false。

  Number.isNaN()用来检查一个值是否为NaN。

console.log(Number.isNaN(NaN))    //true
console.log(Number.isNaN(0.1))    //false
console.log(Number.isNaN('15'))    //false
console.log(Number.isNaN(true))    //false
console.log(Number.isNaN(9/NaN))    //true
console.log(Number.isNaN('true'/0))    //true
console.log(Number.isNaN('true'/'true'))    //true

  如果参数类型不是NaN,Number.isNaN一律返回false。

  他们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,在进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false,

Number.isNaN()还有对NaN才返回true,非NaN一律返回false。

1 console.log(Number.isNaN(NaN))    //true
2 console.log(isNaN(NaN))    //true
3 console.log(Number.isNaN('NaN'))    //false
4 console.log(isNaN('NaN'))    //true
5 
6 console.log(Number.isFinite(6))        //true
7 console.log(isFinite(6))    //true
8 console.log(Number.isFinite('6'))    //false
9 console.log(isFinite('6'))    //true

4.Number.parseInt(),Number.parseFloat()

  ES6将全局方法parseInt()和parseFloat(),移植到Number对象上,行为完全保持不变。

1 console.log(parseInt("12.34"))    //12
2 console.log(parseFloat("12.34#"))    //12.34
3 
4 console.log(Number.parseInt("12.34"))    //12
5 console.log(Number.parseFloat("12.34#"))    //12.34
6 
7 console.log(Number.parseInt === parseInt)    //true
8 console.log(Number.parseFloat === parseFloat)    //true

  上述代码可发现,虽然移植到Number上,但是输出结果、操作是没有什么变化的。

5.Number.isInteger()

  Number.isInteger()用来判断一个数值是否为整数(如果精度要求较高,不推荐使用)

console.log(Number.isInteger(12.1))    //false
console.log(Number.isInteger(12))    //true
console.log(Number.isInteger(12.0))    //true

  值得注意的是12.0和12位一个值,所以判断整数为true。

  如果参数不是数值,Number.isInteger返回false。

1 console.log(Number.isInteger())    //false
2 console.log(Number.isInteger('12'))    //false
3 console.log(Number.isInteger(null))    //false
4 console.log(Number.isInteger(true))    //false

  注意,由于JavaScript采用IEEE 754标准,数值存储为64位双精度格式,数值精度最多可以达到53个二进制位(一个隐藏位与52个有效位)。如果数值的精度超过这个限度,第54位及后面的位数就会被丢弃,这种情况下,Number.isInteger可能会误判。

console.log(Number.isInteger(3.0000000000000002)) //true

  上述代码可发现,Number.isInteger的参数明明不是整数,但是判定结果却显示为true。原因是因为小数位数的精度达到了小数点后16个十进制位,转成二进制位就超过了53个二进制位,导致最后的2被丢弃了。

  类似的情况还有,当一个数值的绝对值小于Number.MIN_VALUE(5E-324),即小于JavaScript能够分辨的最小值,会被自动转为0.这是,Number.isInteger也会误判。

console.log(Number.isInteger(5E-325))    //true
console.log(Number.isInteger(5E-324))    //false

  上述代码中由于5E-325的值太小,会被自动转为0,故而返回true。

6.Number.EPSILON 

  ES6在number对象上面,新增一个极小的常量Number.EPSILON。根据规格,他表示1与大于1的最小浮点数之间的差。

  对于64位浮点数来说,大于1的最小浮点数相当于二进制的1.00...001,小数点后面有连续51个零。这个值减去1之后,就等于2的-52次方。

console.log(Number.EPSILON) ///2.220446049250313e-16
console.log(Number.EPSILON.toFixed(20))//0.00000000000000022204
console.log(Number.EPSILON === Math.pow(2,-52)) //true

  Number.EPSILON实际上是JavaScript能够表示的最小精度。误差如果小于这个值,就认为已经没有意义了,即不存在误差。

  引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不准确的。

console.log(0.1+0.2) //0.30000000000000004
console.log(0.1+0.2-0.3) //5.551115123125783e-17

  上面的代码也说明了,为什么0.1+0.2与0.3得到的结果为false。

console.log(0.1+0.2==0.3) //false

  Number.EPSILON可以用来设置“能够接受的误差范围”。比如,误差范围设置2的-50次方(即Number.EPSILON&Math.pow(2,2)),即如果两个浮点数的差小于这个值,我们认为这两个浮点数相等。

  因此,Number.EPSILON的实质是一个可以接受的最小误差范围(在判断时可以写方法进行校验)。如下

function withinErrorMargin(left,right){
    return Math.abs(left-right)<Number.EPSILON*Math.pow(2,2);
}
console.log(0.1+0.2 == 0.3) //false
console.log(withinErrorMargin(0.1+0.2,0.3)) //true
console.log(1.1+1.3 == 2.4)    //false
console.log(withinErrorMargin(1.1+1.3,2.4))    //true

7.安全整数和Number.isSafeInteger()

  javaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。

console.log(Math.pow(2,53)) //9007199254740992
console.log(9007199254740992) //9007199254740992
console.log(9007199254740993) //9007199254740992
console.log(Math.pow(2,53) == Math.pow(2,53)+1) //true

  上述代码汇总,超出2的53次方后,一个数就不准确了。

  ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。

console.log(Number.MAX_SAFE_INTEGER) //9007199254740991
console.log(Number.MIN_SAFE_INTEGER) //-9007199254740991
console.log(Number.MAX_SAFE_INTEGER == Math.pow(2,53)-1) //true
console.log(Number.MIN_SAFE_INTEGER  == Math.pow(-2,53)+1) //true

  Number.isSafeInteger()则是用来判断一个整数是否落在了上面所说的范围之内。

console.log(Number.isSafeInteger("a")) //false
console.log(Number.isSafeInteger(null))    //false
console.log(Number.isSafeInteger(NaN))    //false
console.log(Number.isSafeInteger(Infinity))    //false
console.log(Number.isSafeInteger(-Infinity))    //false

console.log(Number.isSafeInteger(3))    //true
console.log(Number.isSafeInteger(1.2))    //false
console.log(Number.isSafeInteger(9007199254740990)) //true
console.log(Number.isSafeInteger(9007199254740992))    //false

console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1)) // false
console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER)) // true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)) // true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)) // false

  这个函数的实现很简单,就是跟安全整数的两个边界值进行比较。

Number.isSafeInteger = function (n){
    return (typeof n === 'number' && Math.round(n) === n 
    && Number.MIN_SAFE_INTEGER <= n && Number.MAX_SAFE_INTEGER >=n)
}

   实际使用这个函数时,需要注意。验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。

console.log(Number.isSafeInteger(9007199254740993))//false
console.log(Number.isSafeInteger(990))    //true
console.log(Number.isSafeInteger(9007199254740993-990)) //true
console.log(9007199254740993-990) //9007199254740002实际结果应该为9007199254740003

   上述代码可以发现,我们在进行运算时,应该对参与运算的所有值都进行校验,而不是仅仅只是对结果进行校验。

9.Math对象的扩展

  ES6在Math对象上新增了17个与数学相关的方法。所以这些方法都是静态方法,只能在Matj对象上调用。

  (1.)Math.trunc()

    Math.trunc方法用于去除一个数的小数部分,返回整数部分。

console.log(Math.trunc(4.1))    //4
console.log(Math.trunc(-4.1))    //-4
console.log(Math.trunc(5.9))    //5
console.log(Math.trunc(-5.9))    //-5
console.log(Math.trunc(0.1))    //0
console.log(Math.trunc(-0.1))    //-0

    对于非数值,Math.trunc内部使用Number方法将其先转为数值。

console.log(Math.trunc('4.1'))    //4
console.log(Math.trunc(true))    //1
console.log(Math.trunc(false))    //0
console.log(Math.trunc(null))    //0
console.log(Math.trunc(undefined))    //NaN
console.log(Math.trunc("哈哈"))    //NaN

  (2.)Math.sign()

    Math.sign方法用来判断一个数是否为正数、负数、还是零。对于非数值,会将其先转化为数值。其返回值为

    参数为正数,返回+1

    参数为负数,返回-1

    参数为0,返回0

    参数为-0,返回-0

    其他值,返回NaN。具体实例如下

console.log(Math.sign(-5))    //-1
console.log(Math.sign(5))    //1
console.log(Math.sign(0))    //0
console.log(Math.sign('31'))    //1
console.log(Math.sign(NaN))        //NaN
console.log(Math.sign("哈哈"))    //NaN

  (3.)Math.cbrt()

    Math.cbrt()方法用于计算一个数的立方根,对于非数值,回显将其转为数值,再进行计算。

console.log(Math.cbrt(-5))    //-1.709975946676697
console.log(Math.cbrt(5))    //1.709975946676697
console.log(Math.cbrt(0))    //0
console.log(Math.cbrt('31'))    //3.1413806523913927
console.log(Math.cbrt(NaN))        //NaN
console.log(Math.cbrt("哈哈"))    //NaN

  (4.)Math.clz32()

    Math.clz32()方法将参数转为32位无符号整数的形式,然后返回这个32位值里面有多少个前导0。

    console.log(Math.clz32(0))        //32  
    console.log(Math.clz32(1))        //31
    console.log(Math.clz32(2))        //30
    console.log(Math.clz32(0b01000000000000000000000000000000))     //1

    上述代码中,0的二进制还是0,故有32个前导0;1的二进制为1,有31位前导0;2的二进制为10,有30位前导0;0b表示的是二进制的意思,故有1个前导0。

    clz32这个函数来自于count leading zero bits in 32-bit binary representation of a number(计算一个数字的32位二进制的前导0)的个数的缩写。

    左移运算符(<<)和Math.clz32方法直接相关,例下: 

    console.log(Math.clz32(1))        //31
    console.log(Math.clz32(1<<1))        //30
    console.log(Math.clz32(1<<2))        //29
    console.log(Math.clz32(1<<3))        //28

    对于小数来说,Math.clz32方法只考虑整数部分。如下:

    console.log(Math.clz32(1.2))        //31
    console.log(Math.clz32(1.6))        //31
    console.log(Math.clz32(2.1))        //30
    console.log(Math.clz32(2.6))        //30

    对于空值和其他类型的值,使用Math.clz32会将他们先转换为数值,再进行运算。如下:

    console.log(Math.clz32())        //31
    console.log(Math.clz32(NaN))        //32
    console.log(Math.clz32(null))        //32
    console.log(Math.clz32(undefined))        //32
    console.log(Math.clz32(true))        //31
    console.log(Math.clz32(false))        //32
    console.log(Math.clz32('1'))        //31
    console.log(Math.clz32('asdasd'))        //32
    console.log(Math.clz32('asdasd'))        //32

  (5.)Math.imul()

    Math.imul方法是将两个32位带符号整数相乘,返回一个带符号的整数。

    console.log(Math.imul(-1,-1))        //1
    console.log(Math.imul(-1,5))        //-5
    console.log(Math.imul(-5,6))        //-30
    console.log(Math.imul(-5,5))        //-25

    如果只考虑最后32位,大多数情况下计算都是准确的,即Math.imul(x,y)与x*y是一样的。但在超过32位时,超出部分会被过滤掉(JavaScript会将超出2的53次方的数据无法准确显示),使用这个方法却可以返回正确的最低位运算。如下:

    console.log((0x7fffffff*0x7fffffff)|0) //0
    console.log(Math.imul(0x7fffffff,0x7fffffff))    //1

    上述代码,可以发现第一个两数相乘,正常来说最低位一定是1,但由于结果大于2的53次方,由于JavaScript无法显示超出精度的数据,故最低位为0,当使用Math.imul时,最低位依旧为1。

  (6.)Math.fround()

    Math.found()返回一个数的32位单精度浮点数的形式。

    对于32位单精度格式来说,数值精度是24个二进制位,所以对于 -2的24次方 至 2的24次方 之间的整数(不含两个端点),返回结果与参数本身一致。

    console.log(Math.fround(1))    //1
    console.log(Math.fround(2))    //2
    console.log(Math.fround(2**24-1)) //16777215
    console.log(Math.fround(2**24)) //16777216
    console.log(Math.fround(2**24+1)) //16777216

    上述代码中,当参数>=2的24次方时,就会发现精度丢失。

    对于NaN和Infinity,Math.fround()方法会返回原值。对于其他类型的数据,Math.fround()会将其转化为数值,然后在进行运算。如下:

    console.log(Math.fround(NaN)) //NaN
    console.log(Math.fround(Infinity)) //Infinity
    console.log(Math.fround(11)) //11
    console.log(Math.fround('11')) //11
    console.log(Math.fround('asd')) //NaN
    console.log(Math.fround(null))    //0
    console.log(Math.fround(undefined))    //NaN

      Math.fround()方法的主要作用,是吧64位双精度浮点数转为32位单精度浮点数。如果小数超过24位二进制,这返回的数据就不和原始数据不同。

  (7.)Math.hypot()

    Math.hypot返回所有参数的平方和的平方根。例:

    console.log(Math.hypot(3,4)) //5
    console.log(Math.hypot(2,6))    //6.324555320336759
    console.log(Math.hypot(2,2,2))    //3.4641016151377544
    console.log(Math.hypot(2,2,null)) //2.8284271247461903
    console.log(Math.hypot(2,2,undefined))    //NaN
    console.log(Math.hypot(2,2,NaN))    //NaN
    console.log(Math.hypot(2,2,'dd'))    //NaN
    console.log(Math.hypot(2,2,'2'))    //3.4641016151377544

  如果参数不是数值,Math.hypot会先将其转为数值,再进行运算,如果参数其中一个不是数值,这最终结果为NaN。

  (8.)Math.expm1()

    Math.expm1()方法为e的x方-1,即Math.exp(x)-1。

    console.log(Math.expm1(-1)); //-0.6321205588285577
    console.log(Math.expm1(0));    //0
    console.log(Math.expm1(1)); //1.718281828459045

    对于没有部署这个方法的环境,可使用以下代码:

    Math.expm1 = Math.expm1||function(x){
      return Math.exp(x)-1
    }

  (9.)Math.log1p()

    Math.log1p(x)方法返回1+x的自然对数,即Math.log(1+x),如果x<-1,则返回NaN。

        console.log(Math.log1p(2)) //1.0986122886681096
        console.log(Math.log1p(-1))    //-Infinity
        console.log(Math.log1p(-3))    //NaN
        console.log(Math.log1p('2'))    //1.0986122886681096
        console.log(Math.log1p('aa'))    //NaN
        console.log(Math.log1p(null))    //0
        console.log(Math.log1p(undefined))    //NaN
        console.log(Math.log1p(true))    //0.6931471805599453

    对于没有部署这个方法的环境,可使用一下代码:

Math.log1p = Math.log1p||function(x){
       return Math.log(1+x)  
}

  (10.)Math.log10()

    Math.log10(x)方法返回以10为底x的对数,当x<0时,返回值为NaN。如下

        console.log(Math.log10(1))    //0
        console.log(Math.log10(0))    //-Infinity
        console.log(Math.log10(-1))    //NaN
        console.log(Math.log10(NaN))    //NaN
        console.log(Math.log10(true))    //0
        console.log(Math.log10(null))    //-Infinity
        console.log(Math.log10(undefined))    //NaN
        console.log(Math.log10('asd'))    //NaN

    对于没有部署这个方法的环境,可使用一下代码:

Math.log10 = Math.log10||function(x){
            return Math.log(x)/Math.LN10
        }

  (11.)Math.log2()

    Math.log2(x)方法返回以2为底x的对数,当x<0时,返回值为NaN。如下

        console.log(Math.log2(1))    //0
        console.log(Math.log2(2))    //1
        console.log(Math.log2(4))    //2
        console.log(Math.log2('4'))    //2
        console.log(Math.log2(null))    //-Infinity
        console.log(Math.log2(undefined))    //NaN
        console.log(Math.log2(NaN))    //NaN
        console.log(Math.log2('sa'))    //NaN

对于没有部署这个方法的环境,可使用一下代码: 

Math.log10 = Math.log10||function(x){
            return Math.log(x)/Math.LN2
        }

   (12.)双曲函数方法

    Math.sinh(x) 返回x的双曲正弦

    Math.cosh(x) 返回x的双曲余弦

    Math.tanh(x) 返回x的双曲正切

    Math.asinh(x) 返回x的反双曲正弦

    Math.acosh(x) 返回x的反双曲余弦

    Math.atanh(x) 返回x的反双曲正切

10.指数运算符

  ES2016 新增了一个指数运算符(**)。

        console.log(2**2)    //4
        console.log((-2)**2)    //4
        console.log((-2)**3)    //-8

  值得注意的是,这个运算符的特点是右结合,多个指数运算符连用时,从右边开始计算。

console.log(2**1**2)    //2

  指数运算符可以和等号结合,形成新的赋值运算符(**=)。

        let a = 2;
        a**=2    //4
        console.log(a**=2)    //16   4的平方
        console.log(a**=3)    //4096   16的三次方

11.BigInt数据类型

  (1.)简介

    JavaScript所有数字都保存成64位浮点型,这给数值的表示带来了两大限制。一是数值的精度只能到53个二进制位(相当于16个十进制位),大于这个范围的整数,JavaScript是无法精确表示的,这使得JavaScript不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScpript无法表示,会返回Infinity。

    console.log(2**1024)    //Infinity

     ES2020引入了一种新的数据类型BigInt(大整数),来解决这个问题。BigInt只能用来表示整数,且没有位数限制,也可以进行运算。为了跟Number类型区分,BigInt类型后面必须增加n。

        const a = 123456789n;
        const b = 987456312n;
        console.log(a*b); //121908185557302168n
        const a1 = 123456789;
        const b1 = 987456312;
        console.log(a1*b1); //121908185557302180

    BigInt与Number类型是不一样的,也不能进行比较。

    console.log(123===123n)    //false

    typeof对于BigInt数据返回的类型为BigInt。

  (2.)BigInt对象

    JavaScript原生提供了BigInt对象,可以用作构造函数生成BigInt类型的数值。可以将其他类型的值转为BigInt,和Number()的区别在于,它转换的参数必须是可以正常转换为整数的(参数也不能为小数),否则会报错。

     console.log(BigInt(123))    //123n
        console.log(BigInt('123'))    //123n
        console.log(BigInt(false))    //0n
        console.log(BigInt(true))    //1n
        console.log(BigInt('asd'))    //cannot convert asd to a BigInt
        console.log(BigInt(null))    //cannot convert null to a BigInt
        console.log(BigInt(undefined))    //cannot convert undefined to a BigInt
        console.log(BigInt(1.23))    //The number 1.23 cannot be converted to a BigInt because it is not an integer

    BigInt对象继承了Object对象的两个实例方法。

        BigInt.prototype.toString();
        BigInt.prototype.valueof();

    它还继承了Number对象的一个实例方法。

      BigInt.prototype.toLocaleString()

    此外,它还提供了三个静态方法。

      BigInt.asUintN(width, BigInt): 给定的 BigInt 转为 0 到 2width - 1 之间对应的值。

      BigInt.asIntN(width, BigInt):给定的 BigInt 转为 -2width - 1 到 2width - 1 - 1 之间对应的值。

      BigInt.parseInt(string[, radix]):近似于Number.parseInt(),将一个字符串转换成指定进制的 BigInt。

  (3.)转换规则

    可以使用Boolean()、Number()、String()这三个方法,将BigInt可以转为布尔值、数值和字符串类型。

            console.log(Boolean(1n))    //true
            console.log(Boolean(0n))    //false
            console.log(String(1n))        //'1'
            console.log(Number(1n))        //1

    取反运算符(!)会将BigInt转为布尔值。

            console.log(!0n)    //true
            console.log(!1n)    //false
            console.log(!2n)    //false

 

 

  

 

      

       

      

  

posted @ 2022-10-24 17:26  奔跑的哈密瓜  阅读(69)  评论(0编辑  收藏  举报