5. Number

ECMAScript 有两种内置的数值类型:Number 和 BigInt

按照 ECMAScript 标准的规定,JavaScript 中的所有算术都要使用双精度浮点算术来完成。

1. 通用知识

关于双精度浮点数,需要理解以下内容:

  1. 浮点数的四类常量:最大最小正数、最大最小安全整数、最小间隔、特殊值

内容较多,所以分成多篇在《计算机基础》的浮点数章节中进行详细的介绍。

2. Number 类型

JavaScript 里的 Number 类型是一个双精度 64-bit 二进制格式 IEEE 754 值,这意味着它能表示小数,但在存储上会有一些限制,比如算术上需要四舍五入精度上只保留大约 17 个有效数字

a double-precision 64-bit binary format IEEE 754 value

2.1 浮点数

在 JavaScript 代码中,字面量 35 本质上是个浮点数而不是整数,在日常使用中是没有单独的整数类型的。现在有了个 BigInt 类型,但它并不是为了在日常使用中取代 Number 的。

除了表示浮点数之外,Number 类型还有三个符号值:+Infinity-InfinityNaN(Not a Number)。

2.2 范围

  • 能存储 [210742^{-1074}, 210242^{1024}] 之间的正浮点数和 [-210242^{1024}, -210742^{-1074}] 之间的负浮点数:超出此范围的数值会被替换成 ±Infinity±0

  • 但只能安全地存储 ±(2532^{53}- 1) 范围内的整数:超出此范围的数值,将不能被安全地表示

关于这两个范围涉及的具体值的解释和相关常量,详见双精度浮点数中的四类常量

2.3 NaN

NaN,Not a Number,非数值。

当算术运算的结果不能表示为数值时,通常会遇到 NaN。以下五类操作会返回 NaN

  1. 无法解析的数值,比如 parseInt('hello'), Number(undefined)

  2. 结果不是实数的数学运算,比如 Math.sqrt(-1)

  3. 操作数是 NaN,比如 7**NaN

  4. 不确定形式,比如 0*Infinity, undefined+undefined

  5. 对于不是加法的运算,只要涉及到了字符串就会返回 NaN,比如 'foo'/2

NaN 是全局对象上的一个属性。在现代浏览器中,NaN 是不可配置、不可写的。

NaN = 'test';
console.log(NaN); // NaN,值未变

通常,我们很少在程序中使用 NaN 变量或是去覆盖它,当然也不建议这么做。

2.4 字面量表示

Number 可以用字面量表示,比如 1e3, 08, 0755, 0o755, 0b111, 0x7fe 等。

这部分内容,详见数值的字面量表示

3. Number 对象

Number 对象是 Number 原始值的对象包装器,用来表示和操作数值类型。

3.1 构造器

  1. new Number() 返回 Number 对象,搭配 new 关键字作为构造函数

  2. Number() 返回 Number 数值,直接当函数用可做类型转换,如果不能转换就返回NaN

Number() 作为函数使用,很常见。如下:

// 类型转换
Number("123"); // 123
Number("0b111"); // 7
Number("0o111"); // 73
Number("0x111"); // 273

Number("unicorn"); // NaN
Number(undefined); // NaN

Number("123"); // 123
Number("123") === 123; // true
Number("12.3"); // 12.3
Number("12.00"); // 12
Number("123e-1"); // 12.3
Number(""); // 0
Number(null); // 0
Number("0x11"); // 17
Number("0b11"); // 3
Number("0o11"); // 9
Number("foo"); // NaN
Number("100a"); // NaN
Number("-Infinity"); // -Infinity

Number() 加关键词 new 当做构造函数使用,很少见,也不推荐。原因如下:

// new Number() 很少使用
let numObj = new Number(35);
numObj == 35;  // true
numObj === 35; // false,和字面量数字不全等,因为类型不同
typeof numObj === 'object'; // false
typeof 35 === 'number';     // true

3.2 静态属性

  1. 最大正数和最小正数

    • Number.MAX_VALUE 可表示的最大正数

    • Number.MIN_VALUE 可表示的最小正数,即最接近零的正数(实际上并不为零)

  2. 最大安全整数和最小安全整数

    • Number.MAX_SAFE_INTEGER 最大安全整数,2532^{53} - 1

    • Number.MIN_SAFE_INTEGER 最小安全整数,-(2532^{53} - 1)

  3. 正负无穷大(溢出时返回)

    • Number.NEGATIVE_INFINITY 表示负无穷大的特殊值

    • Number.POSITIVE_INFINITY 表示无穷大的特殊值

  4. Number.EPSILON 两个可表示的数值之间的最小间隔

  5. Number.NaN

关于以上静态属性的含义和值,详见双精度浮点数中的四类常量

最后一个,Number.prototype 允许向 Number 对象添加属性。

3.3 静态方法

  1. 布尔判断

    • Number.isNaN()

    • Number.isFinite()

    • Number.isInteger()

    • Number.isSafeInteger()

  2. 类型转换

    • Number.parseFloat(string)

    • Number.parseInt(string, [radix])

3.4 实例方法

Number.prototype.xxx

3.4.1 通用方法

  1. valueOf() 返回原始值

  2. toString([radix]) 返回以指定基数表示的字符串

  3. toLocaleString([locales [, options]])

3.4.2 常用操作

  1. toPrecision(precision) 返回指定精度的数值字符串

  2. toFixed(digits) 小数点后保留固定位数(using fixed-point notation,使用定点符号)

  3. toExponential(fractionDigits) 返回以指数表示法表示的数值字符串

4. 注意事项

4.1 判断一个值是否为 NaN

NaN 是 JavaScript 中唯一不等于自身的值。

NaN == NaN;  // false
NaN === NaN; // false

当我们想判断一个值是不是 NaN 的时候,可以使用 isNaN()Number.isNaN(),或者是进行下自我比较,因为只有 NaN 不等于自身。代码如下:

isNaN(NaN); // true
isNaN(Number.NaN); // true
Number.isNaN(NaN); // true
Number.isNaN(Number.NaN); // true

// 和自己比较
function myIsNaN(x) {
    return x !== x;
}
myIsNaN(1); // false
myIsNaN(NaN); // true
myIsNaN(Number.NaN); // true

4.2 isNaN()Number.isNaN()

它两的区别在于:当一个非 NaN 的值经过强制类型转换之后值是 NaN 时,isNaN() 是返回 true,而 Number.isNaN() 是返回 false。如下:

isNaN('hello'); // true
Number.isNaN('hello'); // false

也就是说 Number.isNaN() 只有当值是 NaN 时才会返回 true。

基于同样的原因,当参数是个 BigInt 值的时候,它两的表现也不一样。如下:

isNaN(1n); // TypeError: Cannot convert a BigInt value to a number
Number.isNaN(1n); // false

此外,数组的有些方法找不到 NaN 值,而有些方法可以。比如:

let scores = [80, 90, NaN, 100];
scores.indexOf(NaN); // -1 未找到
scores.includes(NaN); // true 能找到
scores.findIndex(x => Number.isNaN(x)); // 2

4.3 判断是否相等

对于非整数的计算结果,不能用 ===== 进行比较,正确的比较方法是检测相差的微小值。比如:

0.1 + 0.2 == 0.3; // false
0.1 + 0.2 === 0.3; // false
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true

5. 主要参考

Last updated