ES6学习笔记(九)数值的扩展

在ES6中,对于数值的扩展,主要是扩展了进制的表示方法,在Number对象和Math对象上增加了一些常量和方法,还有增加了指数运算符。

二进制和八进制的表示法


ES6提供了二进制和八进制的写法,在ES5中,虽然也有在数字前面加一个0来表示八进制的写法,但在严格模式下会报错,而ES6提供的0b,0o即使在严格模式下也不会报错。

(function (){
    console.log(010);
})()
//8


(function (){
    'use strict'
    console.log(010);
})()
//Octal literals are not allowed in strict mode.


(function (){
    'use strict'
    console.log(0o10);
})()
//8


(function (){
    'use strict'
    console.log(0b10);
})()
//2

使用Number方法可以将二进制和八进制转化为十进制

Number(0b111)//7
Number(0o111)//73

Number的扩展


新增的常量

ES6在Number对象上增加了三个常量,上下限和极小值

Number.EPSILON

表示 1 与大于 1 的最小浮点数之间的差。

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

Number.EPSILON === Math.pow(2, -52)// true

Number.EPSILON// 2.220446049250313e-16

Number.EPSILON.toFixed(20)// "0.00000000000000022204"

Number.EPSILON表示了JavaScript中的最小精度,误差如果小于该值,就可视为没有误差了。

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

5.551115123125783e-17 < Number.EPSILON * Math.pow(2, 2)// true

可以用下面的方法来判断两个数的差是否在自己定义的精度内

function withinErrorMargin (num1, num2,accuracy) {//num1和num2为计算的数,accuracy为设置的精度

  return Math.abs(num1 - num2) < Number.EPSILON * Math.pow(2, 52-accuracy);

}//如果在自己设定的精度内,则返回true,否则返回false

Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER

JavaScript 能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。而Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量用来表示这个范围的上下限。

Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1// true

Number.MAX_SAFE_INTEGER === 9007199254740991// true

Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER// true

Number.MIN_SAFE_INTEGER === -9007199254740991// true

对数值的判断方法

在ES6的判断数值的下列方法中,如果传入的参数是非数值,会返回一个false

Number.isFinite()

该方法用于判断一个数是否为有限的,即不是Infinite,NaN也会返回false。

Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false

Number.isNaN()

该方法用于判断一个数是否是NaN

Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(Infinity) //false

对于上面的两个方法,在ES6中有对应的全局方法isFinite和isNaN,不同的是,全局方法的调用会先将参数传入Number方法中再判断其是否为有限和是否为NaN,所以对有些字符串会返回true

isFinite('1')//true

Number.isFinite('1')//false

isNaN('NaN')//true

Number.isNaN('NaN')//false

Number.isInteger

Number.isInteger()用来判断一个数值是否为整数。

Number.isInteger(25) // true

Number.isInteger(25.1) // false

然而,这个方法在传入某些带小数点的数值时也会返回true

1.小数点后面的值为0(实际上还是整数)

2.浮点数的精度小数点后16个十进制位,转成二进制位超过了53个二进制位,而JavaScript数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位),如果超过这个限度,后面的部分就会被抛弃,这个时候isInteger会误判

Number.isInteger(25) // true

Number.isInteger(25.0) // true

Number.isInteger(3.0000000000000002) // true

还有一种情况,如果传入的数值参数的绝对值小于Number.MIN_VALUE(Number.MIN_VALUE=5E-324,为JavaScript能够分辨的最小值,小于该值会被自动转为0)

5e-324===0// false

5e-325===0// true

Number.isInteger(5E-324) // false

Number.isInteger(5E-325) // true

Number.isSafeInteger

IsSafeInteger方法用来判断一个整数是否在在-2^53到2^53之间(不含两个端点)即在Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量之间。

Math.pow(2, 53) // 9007199254740992

9007199254740992  // 9007199254740992

9007199254740993  // 9007199254740992



Math.pow(2, 53) === Math.pow(2, 53) + 1

要注意的是,由于超出这个范围的运算是不会返回正确的结果的,所以如果要验证一个式子最后返回的是不是在这个上下限之间,不仅要验证结果,还有验证参与运算的数,否则可能会是计算结果出错才导致最后返回的结果为true。

 

全局方法的移植

在ES6中,将全局中的parseInt和parseFloat方法移植到Number对象上,行为完全保持不变,这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。

Number.parseInt === parseInt // true

Number.parseFloat === parseFloat // true

Math的扩展


ES6中在Math 对象上新增了 17 个与数学相关的方法。主要是解决了一些精度计算上的问题,增加一些超出精度的数的计算方法,判断数值的方法,处理浮点数的方法,四个对数方法和六个双曲线方法。所有这些方法都是静态方法,只能在 Math 对象上调用,其中传入的参数如果不是数值的话,会调用Number方法后将返回的值作为参数传入,下面的参数默认为已经用Number方法处理过的参数。

Math.trunc

该方法用于去除一个数的小数部分,返回整数部分

Math.trunc(4.1) // 4

Math.trunc(4.9) // 4

Math.trunc(-4.1) // -4

Math.trunc(-4.9) // -4

Math.trunc(-0.1234) // -0

Math.trunc('123.456') // 123

Math.trunc(true) //1

Math.trunc(false) // 0

Math.trunc(null) // 0

Math.trunc(NaN); // NaN

Math.trunc('foo'); // NaN

Math.trunc(); // NaN

Math.trunc(undefined) // NaN

对于没有部署这个方法的环境,可以用下面的代码模拟。

Math.trunc = Math.trunc || function(x) {
    return x < 0 ? Math.ceil(x) : Math.floor(x);
};

这个方法对负数向上取整,对整数向下取整,最后返回的即为一个数的整数部分

Math.sign

Math.sign方法用来判断一个数到底是正数、负数、还是零

  • 正数  :返回+1;
  • 负数  :返回-1;
  • 0     :返回0;
  • -0    :返回-0;
  • 其他值:返回NaN。
Math.sign(-5) // -1

Math.sign(5) // +1

Math.sign(0) // +0

Math.sign(-0) // -0

Math.sign(NaN) // NaN

Math.sign('')  // 0

Math.sign(true)  // +1

Math.sign(false)  // 0

Math.sign(null)  // 0

Math.sign('9')  // +1

Math.sign('foo')  // NaN

Math.sign()  // NaN

Math.sign(undefined)  // NaN

对于没有部署这个方法的环境,可以用下面的代码模拟。

Math.sign = Math.sign || function(x) {
    x = +x; // convert to a number  
    if (x === 0 || isNaN(x))
        return x;
    return x > 0 ? 1 : -1;
};

这里的x=+x将传入的值变为数值,若传入的为非数值且无法转化为数值,就会返回NaN,而因为0===-0返回true,所以x===0是x为0或为-0都能成立,最后用三目运算符判断传入的数是正数还是负数。

Math.cbrt

该方法用于计算一个数的立方根,Math.pow虽然也能计算立方根,但只能计算非负数的立方根,若传入负数会返回NaN

Math.cbrt(27)//3

Math.cbrt(-27)//-3

Math.cbrt('27') // 3

Math.cbrt('hello') // NaN

对于没有部署这个方法的环境,可以用下面的代码模拟。

Math.cbrt = Math.cbrt || function(x) {
    var y = Math.pow(Math.abs(x), 1/3);
    return x < 0 ? -y : y;
};

该方法其实也就是调用了Math.pow来计算一个数的立方根,但由于Math.pow只能计算非负数的立方根,所以就先将绝对值作为参数传入,在返回时通过判断其原来的正负来返回数值。

Math.clz32

该方法将参数转化为32位无符号整数的形式,返回该数中有多少个前导0

Math.clz32(0) // 32

Math.clz32(1) // 31

Math.clz32(1000) // 22

Math.clz32(0b01000000000000000000000000000000) // 1

Math.clz32(0b00100000000000000000000000000000) // 2

如果传入的数值大于2的32次方,就只计后面的32位有多少个前导0

Math.clz32(Math.pow(2,32))//32

Math.clz32(Math.pow(2,32)+1)//31

Math.pow(2,32)最高位就是1,本该返回0,但是只算后面的32位,后32位都为0,所以返回32,而加一后最低位也为1,所以返回31。

对于小数,Math.clz32方法只考虑整数部分。

Math.clz32(3.2) // 30

Math.clz32(3.9) // 30

左移运算符(<<)与Math.clz32方法直接相关。

Math.clz32(0) // 32

Math.clz32(1) // 31

Math.clz32(1 << 1) // 30

Math.clz32(1 << 2) // 29

Math.clz32(1 << 29) // 2

Math.imul

Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。一般情况下调用该方法和两个数直接相乘是一样的,但如果精度超过32位,直接相乘不能得到正确结果,对于大数相乘的结果,低位的数值往往会不精确。

Math.imul(2, 4)   // 8

Math.imul(-1, 8)  // -8

Math.imul(-2, -2) // 4

0x7fffffff * 0x7fffffff //4611686014132420600

Math.imul(0x7fffffff, 0x7fffffff) // 1

((Math.pow(2,32)-1)*(Math.pow(2,32)-2))|0//0

Math.imul(Math.pow(2,32)-1,Math.pow(2,32)-2)//2

我们知道0x7fffffff的最后一位是1,所以两个相乘最后一位也应该是1,但结果却为4611686014132420600,最后一位是0,明显结果是错误的,而使用Math.imul会将超过32位的数抛弃,而事实上两个0x7fffffff相乘结果刚好位2的64次方-2的33次方+1,所以前面两个数超过32位被抛弃了,只留下后面的1

Math.fround

Math.fround方法返回一个数的32位单精度浮点数形式。

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

Math.fround(0)   // 0

Math.fround(1)   // 1

Math.fround(2 ** 24 - 1)   // 16777215

如果参数的绝对值大于 224,返回的结果便开始丢失精度。

Math.fround(2 ** 24)       // 16777216

Math.fround(2 ** 24 + 1)   // 16777216

对于 NaN 和 Infinity,此方法返回原值。对于其它类型的非数值,Math.fround 方法会先将其转为数值,再返回单精度浮点数。

Math.fround(NaN)      // NaN

Math.fround(Infinity) // Infinity

Math.hypot

该方法用来计算所有参数平方和的平方根

Math.hypot(3, 4);        // 5

Math.hypot(3, 4, 5);     // 7.0710678118654755

Math.hypot();            // 0

Math.hypot(NaN);         // NaN

Math.hypot(3, 4, 'foo'); // NaN

Math.hypot(-3);          // 3

对数方法

1.Math.expm1

Math.expm1(x)返回 ex - 1,即Math.exp(x) - 1。

Math.expm1(-1) // -0.6321205588285577

Math.expm1(0)  // 0

Math.expm1(1)  // 1.718281828459045

对于没有部署这个方法的环境,可以用下面的代码模拟。

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

2.Math.log1p

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

Math.log1p(1)  // 0.6931471805599453

Math.log1p(0)  // 0

Math.log1p(-1) // -Infinity

Math.log1p(-2) // NaN

对于没有部署这个方法的环境,可以用下面的代码模拟。

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

3.Math.log10()

Math.log10(x)返回以 10 为底的x的对数。如果x小于 0,则返回 NaN。

Math.log10(2)      // 0.3010299956639812

Math.log10(1)      // 0

Math.log10(0)      // -Infinity

Math.log10(-2)     // NaN

Math.log10(100000) // 5

对于没有部署这个方法的环境,可以用下面的代码模拟。

Math.log10 = Math.log10 || function(x) {

  return Math.log(x) / Math.LN10;
};

4.Math.log2()

Math.log2(x)返回以 2 为底的x的对数。如果x小于 0,则返回 NaN。

Math.log2(3)       // 1.584962500721156

Math.log2(2)       // 1

Math.log2(1)       // 0

Math.log2(0)       // -Infinity

Math.log2(-2)      // NaN

Math.log2(1024)    // 10

Math.log2(1 << 29) // 29

对于没有部署这个方法的环境,可以用下面的代码模拟。

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

双曲线方法

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

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

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

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

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

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

指数运算符


在之前我们要进行指数运算,一般是使用Math.pow来计算的,ES6提供了更为方便简洁的运算符。

指数运算符(**)可以直接进行指数运算,a**b即为a的b次方

2**2//4

2**3//8

要注意的是,指数运算符是右结合的,即多个指数运算符同时计算时,是从右到左算的

2**3**2//512  相当于2**(3**2)

同时指数运算符可以和+-*/这些运算符一样,在后面加个等于号以表示左边的值等于左边的值与右边的值使用该运算符计算得到的结果。

let a=2;
a**=2
console.log(a);//4

参考自阮一峰的《ECMAScript6入门》

猜你喜欢

转载自blog.csdn.net/zemprogram/article/details/86505715