5 新的数值和 Math 特性

本章描述了 ECMAScript 6 的新的数值和 Math 特性。

5.1 概览

现在可以写二进制和八进制数值字面量:

> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8

全局对象 Number 上添加了一些新的属性,尤其是:

  • Number.EPSILON 用于比较浮点数,容忍舍入错误。
  • 一个方法和常量,用于判断 JavaScript 整数是否安全(在有符号的53位范围内,没有精度的损失)。

5.2 新的整型字面量

ECMAScript 5 已经有了十六进制的整型字面量:

> 0x9
9
> 0xA
10
> 0x10
16
> 0xFF
255

ECMAScript 6 引入了两种新的整型字面量:

  • 0b0B 开头的二进制字面量:
> 0b11
3
> 0b100
4
  • 0o0O 开头的八进制字面量(是的,就是一个零后加大写字母 O ;使用第一种小写字母 o 的形式也是可以的):
> 0o7
7
> 0o10
8

记住方法 Number.prototype.toString(radix) 可用于将数值转换回来:

> (255).toString(16)
'ff'
> (4).toString(2)
'100'
> (8).toString(8)
'10'

5.2.1 八进制字面量的使用场景: Unix 风格的文件权限

在 Node.js 的文件系统模块,有几个函数有 mode 参数,用于指定文件权限,via an encoding that is a holdover from Unix:

  • 权限被指定给三种用户:
    • 用户:文件拥有者
    • 组:跟文件相关的组成员
    • 所有:其余用户
  • 每一种用户,都可以被授予下列权限:
    • r ( read ):此种用户允许读文件
    • w ( write ):此种用户允许改变文件
    • x ( execute ):此种用户允许运行文件

这意味着权限可以通过9位来表示(3种用户,每种用户有三种权限):

用户 所有
权限 r,w,x r,w,x r,w,x
位( Bit ) 8,7,6 5,4,3 2,1,0

某一种类型的用户的权限值占3位:

权限 八进制数字
000 --- 0
001 --x 1
010 -w- 2
011 -wx 3
100 r-- 4
101 r-x 5
110 rw- 6
111 rwx 7

这表明八进制数很适合用来代表所有权限,仅需要3个数字,每个数字代表一种用户的权限。两个例子:

  • 755 = 111,101,101:我可以修改,读取和执行;其他所有人只能读取和执行。
  • 640 = 110,100,000:我可以读取和写入;组成员可以读取;其余用户根本不能获取。

5.2.2 parseInt() 和新的整型字面量

parseInt() 有如下声明:

parseInt(string, radix?)

它对十六进制字面量提供了特殊的支持 - 当满足下述条件的时候,string 参数的 0x(或 0X )前缀会被移除:

  • 未传递 radix 参数或者该参数为0,此时 radix 被设为16。
  • 传递 radix 并为16。

例如:

> parseInt('0xFF')
255
> parseInt('0xFF', 0)
255
> parseInt('0xFF', 16)
255

在所有其他情形下,数字一直解析到第一个非数字的地方:

> parseInt('0xFF', 10)
0
> parseInt('0xFF', 17)
0

parseInt() 并没有对八进制或二进制字面量特殊支持!

> parseInt('0b111')
0
> parseInt('0b111', 2)
0
> parseInt('111', 2)
7

> parseInt('0o10')
0
> parseInt('0o10', 8)
0
> parseInt('10', 8)
8

如果想解析这种字面量,应该使用 Number()

> Number('0b111')
7
> Number('0o10')
8

或者,也可以移除前缀,然后使用 parseInt() ,传入适当的 radix 参数:

> parseInt('111', 2)
7
> parseInt('10', 8)
8

5.3 新的静态 Number 类属性

本节叙述了 ECMAScript 6 中添加的构造器 Number 上的新属性。

5.3.1 预定义的全局函数

四个数值相关的全局函数已经添加到 Number 下面了,这些方法是: isFiniteisNaNparseFloatparseInt 。所有方法都表现得基本跟对应的全局函数一样,但是 isFiniteisNaN 不需要它们的参数是数字。下述子章节解释了所有细节。

5.3.1.1 Number.isFinite(number)

number 是否是一个真正的数字(不为 Infinity-InfinityNaN )?

> Number.isFinite(Infinity)
false
> Number.isFinite(-Infinity)
false
> Number.isFinite(NaN)
false
> Number.isFinite(123)
true

该方法的优点是它并不需要它的参数是数字(全局的那个函数需要):

> Number.isFinite('123')
false
> isFinite('123')
true

5.3.1.2 Number.isNaN(number)

number 是否为 NaN ?通过 === 来检查是奇巧淫技。 NaN 是唯一一个自己不等于自己的值:

> let x = NaN;
> x === NaN
false

因此,下述表达式用于检查是否为 NaN

> x !== x
true

使用 Number.isNaN() 具有更强的自我描述力:

> Number.isNaN(x)
true

Number.isNaN() 也不需要它的参数是数字(全局的那个函数需要):

> Number.isNaN('???')
false
> isNaN('???')
true

5.3.1.3 Number.parseFloatNumber.parseInt

下面两个方法和全局的同名函数效果一样。由于完整性的元婴,它们被添加到 Number 上;现在,所有数字相关的函数都在 Number 上了。

  • Number.parseFloat(string)
  • Number.parseInt(string, radix)

5.3.2 Number.EPSILON

舍入误差在 JavaScript 中是一个问题,尤其是十进制小数。例如,0.1和0.2可以被精确地描述,如果两者相加,并和0.3比较,会有如下结果:

> 0.1 + 0.2 === 0.3
false

在比较浮点数的时候,Number.EPSILON 指定了一个合理的误差范围。它提供了一种更好的方式来比较浮点数值,就像如下函数所示:

function epsEqu(x, y) {
    return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true

5.3.3 Number.isInteger(number)

JavaScript 仅有浮点数(双精度)。相应的,整数是简单的没有小数部分的浮点数。

如果 number 是数字并且没有小数部分,则 Number.isInteger(number) 返回 true

> Number.isInteger(-17)
true
> Number.isInteger(33)
true
> Number.isInteger(33.1)
false
> Number.isInteger('33')
false
> Number.isInteger(NaN)
false
> Number.isInteger(Infinity)
false

5.3.4 安全整数

JavaScript 数字仅有足够的空间存放53位有符号整数。也就是说,在范围-253 < i < 2 53之间的整数 i 是安全的。先暂时解释这究竟意味着什么。下面的属性帮助判断某个 JavaScript 整数是否是安全的:

  • Number.isSafeInterger(number)
  • Number.MIN_SAFE_INTEGER
  • Number.MAX_SAFE_INTEGER

安全整数的概念重点在于数学上的整数在 JavaScript 中如何展示。在范围(-253,253)(除去上下边界)中, JavaScript 整数是安全的:要展示的数学上的整数和范围中的整数是一一对应的。

超过这个范围, JavaScript 整数就不安全了:两个或者多个数学上的整数使用同一个 JavaScript 整数来表示。例如,从253开始, JavaScript 仅能表示数学上的偶数:

> Math.pow(2, 53)
9007199254740992

> 9007199254740992
9007199254740992
> 9007199254740993
9007199254740992
> 9007199254740994
9007199254740994
> 9007199254740995
9007199254740996
> 9007199254740996
9007199254740996
> 9007199254740997
9007199254740996

因此,安全整数能够明确代表某一个数学上的整数。

5.3.4.1 Number 上跟安全整数相关的静态属性

这两个静态的 Number 属性指定了安全整数的上下边界,可能像如下所示的方式定义:

Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;
Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;

Number.isSafeInteger() 用于判定一个 JavaScript 数字是否是安全整数,可以这样来定义该方法:

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

对于给定的值 n ,该函数首先检查 n 是否是数字和整数。如果两次检查都通过了,并且 n 大于等于 MIN_SAFE_INTEGER ,小于等于 MAX_SAFE_INTEGER ,那么 n 就是安全的。

5.3.4.2 什么时候整数运算是正确的?

如何确定整数运算的结果是正确的?例如,下面的结果明显不对:

> 9007199254740990 + 3
9007199254740992

两个操作数都是安全的,但是得到了一个不安全的结果:

> Number.isSafeInteger(9007199254740990)
true
> Number.isSafeInteger(3)
true
> Number.isSafeInteger(9007199254740992)
false

下面的结果依然不正确:

> 9007199254740995 - 10
9007199254740986

这次,结果是安全的,但是其中一个操作数不安全:

> Number.isSafeInteger(9007199254740995)
false
> Number.isSafeInteger(10)
true
> Number.isSafeInteger(9007199254740986)
true

因此,对整数应用操作符 op ,仅当所有操作数和结果是安全的,才能确保结果是正确的。更正式地:

isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)

意味着 a op b 能得到正确的结果。

本节来源 “Clarify integer and safe integer resolution”, Mark S. Miller 发送给 es 讨论邮件列表。

5.4 Math

ECMAScript 6 中的全局对象 Math 有一个新的方法。

5.4.1 各种各样的数值函数

5.4.1.1 Math.sign(x)

返回 x 的符号,-1或+1。如果 xNaN 或者零,则返回 x 。

> Math.sign(-8)
-1
> Math.sign(3)
1

> Math.sign(0)
0
> Math.sign(NaN)
NaN

> Math.sign(-Infinity)
-1
> Math.sign(Infinity)
1

results matching ""

    No results matching ""