编写数字的更多方法
- 语法糖:let billion = 1000000000; let billion = 1_000_000_000; 下划线 _ 扮演了“语法糖”的角色,使得数字具有更强的可读性。JavaScript 引擎会直接忽略数字之间的 _,所以 两个例子其实是一样的。
- 将 "e" 和 0 的数量附加到数字后。就像:123e6 与 123 后面接 6 个 0 相同。
- "e" 后面的负数将使数字除以 1 后面接着给定数量的零的数字。例如 123e-6 表示 0.000123(123 的百万分之一)。
十六进制,二进制和八进制数字
对于不同的数字系统:
- 可以直接在十六进制(0x),八进制(0o)和二进制(0b)系统中写入数字。
- parseInt(str,base) 将字符串 str 解析为在给定的 base 数字系统中的整数,2 ≤ base ≤ 36。
- num.toString(base) 将数字转换为在给定的 base 数字系统中的字符串。
- 十六进制 数字在 JavaScript 中被广泛用于表示颜色,编码字符以及其他许多东西。所以自然地,有一种较短的写方法:0x,然后是数字。
- alert( 0xff ); // 255
- alert( 0xFF ); // 255(一样,大小写没影响)
- 二进制和八进制数字系统很少使用,但也支持使用 0b 和 0o 前缀:
- let a = 0b11111111; // 二进制形式的 255
- let b = 0o377; // 八进制形式的 255
- alert( a == b ); // true,两边是相同的数字,都是 255
只有这三种进制支持这种写法。对于其他进制,应该使用函数 parseInt。
toString(base):变成字符串,并可以指定进制
方法 num.toString(base) 返回在给定 base 进制数字系统中 num 的字符串表示形式。
- base 的范围可以从 2 到 36。默认情况下是 10。
- base=16 用于十六进制颜色,字符编码等,数字可以是 0..9 或 A..F。
- base=2 主要用于调试按位操作,数字可以是 0 或 1。
- base=36 是最大进制,数字可以是 0..9 或 A..Z。所有拉丁字母都被用于了表示数字。
- 对于 36 进制来说,一个有趣且有用的例子是,当我们需要将一个较长的数字标识符转换成较短的时候,例如做一个短的 URL。可以简单地使用基数为 36 的数字系统表示:
- alert( 123456..toString(36) ); // 2n9c
因为 JavaScript 语法隐含了第一个点之后的部分为小数部分。如果我们再放一个点,那么 JavaScript 就知道小数部分为空,不然就出现一个 error。也可以写成 (123456).toString(36)。
Math
- 舍入(rounding)是使用数字时最常用的操作之一。
- Math.floor 向下舍入:3.1 变成 3,-1.1 变成 -2。
- Math.ceil 向上舍入:3.1 变成 4,-1.1 变成 -1。
- Math.round 向最近的整数舍入:3.1 变成 3,3.6 变成 4,中间值 3.5 变成 4。
- Math.trunc(IE 浏览器不支持这个方法)移除小数点后的所有内容而没有舍入:3.1 变成 3,-1.1 变成 -1。
- 以上这些函数涵盖了处理数字小数部分的所有可能方法。但是,如果我们想将数字舍入到小数点后 n 位,该怎么办?有两种方式可以实现这个需求:
- 乘除法:要将数字舍入到小数点后两位,我们可以将数字乘以 100,调用舍入函数,然后再将其除回。
- 函数 toFixed(n) 将数字舍入到小数点后 n 位,并以字符串形式返回结果。请注意 toFixed 的结果是一个字符串。如果小数部分比所需要的短,则在结尾添加零:
let num = 12.34;
alert( num.toFixed(5) ); // "12.34000",在结尾添加了 0,以达到小数点后五位
我们可以使用一元加号或 Number() 调用,将其转换为数字,例如 + num.toFixed(5)。
不精确的计算
- 一个数字以其二进制的形式存储在内存中,一个 1 和 0 的序列。但是在十进制数字系统中看起来很简单的 0.1,0.2 这样的小数,实际上在二进制形式中是无限循环小数。
- 在十进制数字系统中,可以保证以 10 的整数次幂作为除数能够正常工作,但是以 3 作为除数则不能。也是同样的原因,在二进制数字系统中,可以保证以 2 的整数次幂作为除数时能够正常工作,但 1/10 就变成了一个无限循环的二进制小数。
- 使用二进制数字系统无法 精确 存储 0.1 或 0.2,就像没有办法将三分之一存储为十进制小数一样。
- 不仅仅是 JavaScript。许多其他编程语言也存在同样的问题。PHP,Java,C,Perl,Ruby 给出的也是完全相同的结果,因为它们基于的是相同的数字格式。
- 我们能解决这个问题吗?当然,最可靠的方法是借助方法 toFixed(n) 对结果进行舍入:
let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // 0.30
- 请注意,toFixed 总是返回一个字符串。它确保小数点后有 2 位数字。如果我们有一个电子购物网站,并需要显示 ¥ 0.30,这实际上很方便。对于其他情况,我们可以使用一元加号将其强制转换为一个数字:
let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3
- 我们可以将数字临时乘以 100(或更大的数字),将其转换为整数,进行数学运算,然后再除回。当我们使用整数进行数学运算时,误差会有所减少,但仍然可以在除法中得到:
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
- 因此,乘/除法可以减少误差,但不能完全消除误差。
测试:isFinite 和 isNaN
- 还记得这两个特殊的数值吗?
- Infinity(和 -Infinity)是一个特殊的数值,比任何数值都大(小)。
- NaN 代表一个 error。
它们属于 number 类型,但不是“普通”数字,因此,这里有用于检查它们的特殊函数:
- isNaN(value) 将其参数转换为数字,然后测试它是否为 NaN:
alert( isNaN(NaN) ); // true
alert( isNaN("str") ); // true
- 但是我们需要这个函数吗?我们不能只使用 === NaN 比较吗?不好意思,这不行。值 “NaN” 是独一无二的,它不等于任何东西,包括它自身:
alert( NaN === NaN ); // false
- isFinite(value) 将其参数转换为数字,如果是常规数字而不是 NaN/Infinity/-Infinity,则返回 true:
alert( isFinite("15") ); // true
alert( isFinite("str") ); // false,因为是一个特殊的值:NaN
alert( isFinite(Infinity) ); // false,因为是一个特殊的值:Infinity
- 有时 isFinite 被用于验证字符串值是否为常规数字:
let num = +prompt("Enter a number", '');
// 结果会是 true,除非你输入的是 Infinity、-Infinity 或不是数字
alert( isFinite(num) );
- 请注意,在所有数字函数中,包括 isFinite,空字符串或仅有空格的字符串均被视为 0。
- 与 Object.is 进行比较:有一个特殊的内建方法 Object.is,它类似于 === 一样对值进行比较,但它对于两种边缘情况更可靠
- 它适用于 NaN:Object.is(NaN,NaN) === true,这是件好事。
- 值 0 和 -0 是不同的:Object.is(0,-0) === false,从技术上讲这是对的,因为在内部,数字的符号位可能会不同,即使其他所有位均为零。
- * 在所有其他情况下,Object.is(a,b) 与 a === b 相同。
- * 这种比较方式经常被用在 JavaScript 规范中。当内部算法需要比较两个值是否完全相同时,它使用 Object.is(内部称为 SameValue)。
parseInt 和 parseFloat
使用 parseInt/parseFloat 进行“软”转换,它从字符串中读取数字,然后返回在发生 error 前可以读取到的值。
- 使用加号 + 或 Number() 的数字转换是严格的。如果一个值不完全是一个数字,就会失败。唯一的例外是字符串开头或结尾的空格,因为它们会被忽略。
- 从字符串中“读取”数字,直到无法读取为止。如果发生 error,则返回收集到的数字。函数 parseInt 返回一个整数,而 parseFloat 返回一个浮点数。
alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5
alert( parseInt('12.3') ); // 12,只有整数部分被返回了
alert( parseFloat('12.3.4') ); // 12.3,在第二个点出停止了读取
- 某些情况下,parseInt/parseFloat 会返回 NaN。当没有数字可读时会发生这种情况:alert( parseInt('a123') ); // NaN,第一个符号停止了读取
- parseInt(str, radix) 的第二个参数:可指定数字系统的基数,因此 parseInt 还可以解析十六进制数字、二进制数字等的字符串
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效
alert( parseInt('2n9c', 36) ); // 123456
其他数学函数
JavaScript 有一个内建的 Math 对象,它包含了一个小型的数学函数和常量库。
- Math.random() 返回一个从 0 到 1 的随机数(不包括 1)。
- Math.max(a, b, c...) / Math.min(a, b, c...) 从任意数量的参数中返回最大/最小值。
- Math.pow(n, power) 返回 n 的给定(power)次幂。
- Math 对象中还有更多函数和常量,包括三角函数等。