引用的原文地址ES6 入门教程
二进制和八进制表示法
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b
(或0B
)和0o
(或0O
)表示。
0b111110111 === 503 // true
0o767 === 503 // true
从 ES5 开始,在严格模式之中,八进制就不再允许使用前缀0
表示,ES6 进一步明确,要使用前缀0o
表示。
// 非严格模式
(function(){
console.log(0o11 === 011);
})() // true
// 严格模式
(function(){
'use strict';
console.log(0o11 === 011);
})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.
如果要将0b
和0o
前缀的字符串数值转为十进制,要使用Number
方法。
Number('0b111') // 7
Number('0o10') // 8
这个函数的实现很简单,就是跟安全整数的两个边界值比较一下。
Number.isSafeInteger = function (n) {
return (typeof n === 'number' &&
Math.round(n) === n &&
Number.MIN_SAFE_INTEGER <= n &&
n <= Number.MAX_SAFE_INTEGER);
}
实际使用这个函数时,需要注意。验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。
Number.isSafeInteger(9007199254740993)
// false
Number.isSafeInteger(990)
// true
Number.isSafeInteger(9007199254740993 - 990)
// true
9007199254740993 - 990
// 返回结果 9007199254740002
// 正确答案应该是 9007199254740003
上面代码中,9007199254740993
不是一个安全整数,但是Number.isSafeInteger
会返回结果,显示计算结果是安全的。这是因为,这个数超出了精度范围,导致在计算机内部,以9007199254740992
的形式储存。
9007199254740993 === 9007199254740992
// true
所以,如果只验证运算结果是否为安全整数,很可能得到错误结果。下面的函数可以同时验证两个运算数和运算结果。
function trusty (left, right, result) {
if (
Number.isSafeInteger(left) &&
Number.isSafeInteger(right) &&
Number.isSafeInteger(result)
) {
return result;
}
throw new RangeError('Operation cannot be trusted!');
}
trusty(9007199254740993, 990, 9007199254740993 - 990)
// RangeError: Operation cannot be trusted!
trusty(1, 2, 3)
// 3
Math 对象的扩展
ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。
Math.trunc()
Math.trunc
方法用于去除一个数的小数部分,返回整数部分。
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
对于非数值,Math.trunc
内部使用Number
方法将其先转为数值。
Math.trunc('123.456') // 123
Math.trunc(true) //1
Math.trunc(false) // 0
Math.trunc(null) // 0
对于空值和无法截取整数的值,返回NaN
。
Math.trunc(NaN); // NaN
Math.trunc('foo'); // NaN
Math.trunc(); // NaN
Math.trunc(undefined) // NaN
对于没有部署这个方法的环境,可以用下面的代码模拟。
Math.trunc = Math.trunc || function(x) {
return x < 0 ? Math.ceil(x) : Math.floor(x);
};
Math.sign()
Math.sign
方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
它会返回五种值。
- 参数为正数,返回
+1
; - 参数为负数,返回
-1
; - 参数为 0,返回
0
; - 参数为-0,返回
-0
; - 其他值,返回
NaN
。
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
如果参数是非数值,会自动转为数值。对于那些无法转为数值的值,会返回NaN
。
Math.sign('') // 0
Math.sign(true) // +1
Math.sign(false) // 0
Math.sign(null) // 0
Math.sign('9') // +1
Math.sign('foo') // NaN
Math.sign() // NaN
Math.sign(undefined) // NaN
对于没有部署这个方法的环境,可以用下面的代码模拟。
Math.sign = Math.sign || function(x) {
x = +x; // convert to a number
if (x === 0 || isNaN(x)) {
return x;
}
return x > 0 ? 1 : -1;
};
Math.cbrt()
Math.cbrt()
方法用于计算一个数的立方根。
Math.cbrt(-1) // -1
Math.cbrt(0) // 0
Math.cbrt(1) // 1
Math.cbrt(2) // 1.2599210498948732
对于非数值,Math.cbrt()
方法内部也是先使用Number()
方法将其转为数值。
Math.cbrt('8') // 2
Math.cbrt('hello') // NaN
对于没有部署这个方法的环境,可以用下面的代码模拟。
Math.cbrt = Math.cbrt || function(x) {
var y = Math.pow(Math.abs(x), 1/3);
return x < 0 ? -y : y;
};
Math.clz32(3.2) // 30
Math.clz32(3.9) // 30
对于空值或其他类型的值,Math.clz32
方法会将它们先转为数值,然后再计算。
Math.clz32() // 32
Math.clz32(NaN) // 32
Math.clz32(Infinity) // 32
Math.clz32(null) // 32
Math.clz32('foo') // 32
Math.clz32([]) // 32
Math.clz32({}) // 32
Math.clz32(true) // 31
双曲函数方法
ES6 新增了 6 个双曲函数方法。
Math.sinh(x)
返回x
的双曲正弦(hyperbolic sine)Math.cosh(x)
返回x
的双曲余弦(hyperbolic cosine)Math.tanh(x)
返回x
的双曲正切(hyperbolic tangent)Math.asinh(x)
返回x
的反双曲正弦(inverse hyperbolic sine)Math.acosh(x)
返回x
的反双曲余弦(inverse hyperbolic cosine)Math.atanh(x)
返回x
的反双曲正切(inverse hyperbolic tangent)
BigInt数据类型
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity
。
// 超过 53 个二进制位的数值,无法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// 超过 2 的 1024 次方的数值,无法表示
Math.pow(2, 1024) // Infinity
ES2020 引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
const a = 2172141653n;
const b = 15346349309n;
// BigInt 可以保持精度
a * b // 33334444555566667777n
// 普通整数无法保持精度
Number(a) * Number(b) // 33334444555566670000
为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n
。
1234 // 普通整数
1234n // BigInt
// BigInt 的运算
1n + 2n // 3n
BigInt 同样可以使用各种进制表示,都要加上后缀n
。
0b1101n // 二进制
0o777n // 八进制
0xFFn // 十六进制
BigInt 与普通整数是两种值,它们之间并不相等。
42n === 42 // false
typeof
运算符对于 BigInt 类型的数据返回bigint
。
typeof 123n // 'bigint'
BigInt 可以使用负号(-
),但是不能使用正号(+
),因为会与 asm.js 冲突。
-42n // 正确
+42n // 报错
JavaScript 以前不能计算70的阶乘(即70!
),因为超出了可以表示的精度。
let p = 1;
for (let i = 1; i <= 70; i++) {
p *= i;
}
console.log(p); // 1.197857166996989e+100
现在支持大整数了,就可以算了,浏览器的开发者工具运行下面代码,就OK。
let p = 1n;
for (let i = 1n; i <= 70n; i++) {
p *= i;
}
console.log(p); // 11978571...00000000n
参数如果是小数,也会报错。
BigInt(1.5) // RangeError
BigInt('1.5') // SyntaxError
BigInt 继承了 Object 对象的两个实例方法。
BigInt.prototype.toString()
BigInt.prototype.valueOf()
它还继承了 Number 对象的一个实例方法。
BigInt.prototype.toLocaleString()
转换规则
可以使用Boolean()
、Number()
和String()
这三个方法,将 BigInt 可以转为布尔值、数值和字符串类型。
Boolean(0n) // false
Boolean(1n) // true
Number(1n) // 1
String(1n) // "1"
上面代码中,注意最后一个例子,转为字符串时后缀n
会消失。
另外,取反运算符(!
)也可以将 BigInt 转为布尔值。
!0n // true
!1n // false
数学运算
数学运算方面,BigInt 类型的+
、-
、*
和**
这四个二元运算符,与 Number 类型的行为一致。除法运算/
会舍去小数部分,返回一个整数。
9n / 5n
// 1n
几乎所有的数值运算符都可以用在 BigInt,但是有两个例外。
- 不带符号的右移位运算符
>>>
- 一元的求正运算符
+
上面两个运算符用在 BigInt 会报错。前者是因为>>>
运算符是不带符号的,但是 BigInt 总是带有符号的,导致该运算无意义,完全等同于右移运算符>>
。后者是因为一元运算符+
在 asm.js 里面总是返回 Number 类型,为了不破坏 asm.js 就规定+1n
会报错。
BigInt 不能与普通数值进行混合运算。
1n + 1.3 // 报错
果一个标准库函数的参数预期是 Number 类型,但是得到的是一个 BigInt,就会报错。
// 错误的写法
Math.sqrt(4n) // 报错
// 正确的写法
Math.sqrt(Number(4n)) // 2
上面代码中,Math.sqrt
的参数预期是 Number 类型,如果是 BigInt 就会报错,必须先用Number
方法转一下类型,才能进行计算。
asm.js 里面,|0
跟在一个数值的后面会返回一个32位整数。根据不能与 Number 类型混合运算的规则,BigInt 如果与|0
进行运算会报错。
1n | 0 // 报错
其他运算
BigInt 对应的布尔值,与 Number 类型一致,即0n
会转为false
,其他值转为true
。
if (0n) {
console.log('if');
} else {
console.log('else');
}
// else
上面代码中,0n
对应false
,所以会进入else
子句。
比较运算符(比如>
)和相等运算符(==
)允许 BigInt 与其他类型的值混合计算,因为这样做不会损失精度。
0n < 1 // true
0n < true // true
0n == 0 // true
0n == false // true
0n === 0 // false
BigInt 与字符串混合运算时,会先转为字符串,再进行运算。
'' + 123n // "123"