js 中,Number 是一种 定义为 64位双精度浮点型(double-precision 64-bit floating point format) (IEEE 754) 的数字数据类型。在其他编程语言中,有不同的数字类型存在,比如:整型(Integers),单精度浮点型(Floats),双精度浮点型(Doubles),大数(Bignums),不过新的 js 也要有 BigInt 类型数字了。
提到 Number ,有两个概念:全局对象 Number ,和构造函数 Number ,全局对象 Number 由构造函数 Number() 创建。
本文主要涉及全局对象 Number 的几个静态属性。
MAX_SAFE_INTEGER 以及 MIN_SAFE_INTEGER
有一种诡异的接口数据错误,是后端发现自己没有错,但和前端看到的数据就是不一样。这时候往往需要考虑下是不是其中 Number 类型的数据超出范围了。
那么 js 中,整数类型的范围是什么呢?
-(2^53 - 1) 至 2^53 - 1
,包含两个端点。
可以通过 Number 的静态属性 Number.MAX_SAFE_INTEGER
和 Number.MIN_SAFE_INTEGER
来查看:
Number.MAX_SAFE_INTEGER // > 9007199254740991
9007199254740991 + 1 // > 9007199254740992
9007199254740992 + 1 // >9007199254740992
Number.MIN_SAFE_INTEGER // > -9007199254740991
-9007199254740991 - 1 // > -9007199254740992
-9007199254740992 - 1 // > -9007199254740992
9007199254740992 === 9007199254740993 // > true
9007199254740991 === 9007199254740992 // > false
-9007199254740992 === -9007199254740993 // > true
-9007199254740991 === -9007199254740992 // > false
可以看到,对于 -(2^53 - 1)
到 2^53 - 1
范围外的数字,js 并不能做到“安全存储”,也就是无法准确分辨两个不相同的值。
json 解析接口返回的数据时,遇到 js 不能安全存储的值,强制转化为 Number 类型,字段的值就会被破坏。
对于可能超出整数类型范围的字段,一般的解决方案是改为 string 类型输出。
Number 提供了 isSafeInteger()
方法来判断被传递的值是否为安全整数。
Number.isSafeInteger(9007199254740992) // > false
Number.isSafeInteger(9007199254740991) // > true
Number.isSafeInteger(-9007199254740991) // > true
Number.isSafeInteger(-9007199254740992) // > false
不过新的 js 中定义了一种 BigInt 类型的数据,可以表示 大于 2^53 - 1
的数字:
BigInt(Number.MAX_SAFE_INTEGER + 2) + 1n // > 9007199254740993n
Number 类型的数字后面加上 n
可以定义一个 BigInt ,如上例;但 BigInt 和 Number 实例不能进行混合运算,因为 BigInt 变量转换成 Number 变量时可能会丢失精度:
BigInt(Number.MAX_SAFE_INTEGER + 2) + 1
// > VM261:1 Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
at <anonymous>:1:37
MAX_VALUE 以及 MIN_VALUE
需要注意的是,Number.MIN_VALUE
至 Number.MAX_VALUE
并不是 js 能表达的数值的范围,而是正数的范围。
Number.MAX_VALUE
表示 js 能表达的最大正值 ,超过这个数值即 Infinity
。
Number.MAX_VALUE // > 1.7976931348623157e+308
1.7976931348623157e+309 // > Infinity
Number.MIN_VALUE
表示 js 能表达的最小正数,即最接近 0
但不会变成 0
的正数。
Number.MIN_VALUE // > 5e-324
对于负数范围,可以如下表示:
-Number.MAX_VALUE // > -1.7976931348623157e+308 最小负数
-Number.MIN_VALUE // > -5e-324 最大负数
小于 -Number.MAX_VALU
E 的值即为 -Infinity
:
-1.7976931348623157e+309
-Infinity
Infinity
和 -Infinity
也可以用两个静态属性表示:
Number.NEGATIVE_INFINITY // > -Infinity
Number.POSITIVE_INFINITY // > Infinity
NaN
Number.NaN
表示“非数字”,not a number ,编程中很少使用到 Number.NaN
。
全局对象中也有一个 NaN
,同时也有一个 isNaN()
函数。
同样,编程中也很少用 NaN
,毕竟即使 NaN
也是不同于 NaN
自己的,所有的自比较中,也只有 NaN
和自己不等(所以可以通过这个差异来实现 isNaN
函数:function isNaN(v) {return v !== v}
):
NaN === NaN // > false
Number.NaN === NaN // false
我们通常用 isNaN()
或者 Number.isNaN()
来判断一个值是不是 Number 类型:
isNaN(NaN) // > true
isNaN(Number.NaN) // > true
Number.isNaN(NaN) // > true
Number.isNaN(Number.NaN) // > true
那么,isNaN()
和 Number.isNaN()
有区别吗?有,Number.isNaN()
仅当值为 NaN
时才返回 true
,而 isNaN()
,在值为 NaN
或者将值强制转换为数字后能得到 NaN
时,都是 true
。
isNaN('I am a string') // > true
Number.isNaN('I am a string') // > false
EPSILON
Number.EPSILON
属性表示 1
与 Number 可表示的“大于 1 的最小浮点数”之间的差值。
这个静态属性有什么应用场景呢?首先来看一个很典型的例子:
0.1 + 0.2 // > 0.30000000000000004
0.1 + 0.2 === 0.3 // > false
假设 x = 0.1, y = 0.2, z = 0.3
,我们怎么判断 x + y
是否等于 z
?
function isEqualFloat (a, b) {
return Math.abs(a - b) < Number.EPSILON
}
isEqualFloat(x + y, z) // > true
是不是很有意思呢?