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 引入了两种新的整型字面量:
- 以
0b
或0B
开头的二进制字面量:
> 0b11
3
> 0b100
4
- 以
0o
或0O
开头的八进制字面量(是的,就是一个零后加大写字母 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
下面了,这些方法是: isFinite
和 isNaN
, parseFloat
和 parseInt
。所有方法都表现得基本跟对应的全局函数一样,但是 isFinite
和 isNaN
不需要它们的参数是数字。下述子章节解释了所有细节。
5.3.1.1 Number.isFinite(number)
number
是否是一个真正的数字(不为 Infinity
或 -Infinity
或 NaN
)?
> 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.parseFloat
和 Number.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。如果 x
是 NaN
或者零,则返回 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