今天想起了js新增的基本数据类型,就去上网查阅了一番,在此做个总结。
首先我们要了解它是干什么的,BigInt
数据类型的目的是比Number
数据类型支持的范围更大的整数值以任意精度表示整数的能力尤为重要。使用BigInt
,整数溢出将不再是问题。
因为在js中的数字类型是按照IEEE 754-2008
标准的定义,所有数字都以双精度64位浮点
格式表示。因为在IEEE 754
标准下浮点数的舍入模式是一下4类:
最近舍入 | 即向距离最近的浮点数舍入,若存在两个同样接近的数,则选择偶数作为舍入值 |
---|---|
向零舍入 | 又称截断舍入,将多余的精度位截掉,即取舍入后绝对值较小的值 |
正向舍入 | 也称正无穷舍入,即舍入后结果大于原值 |
负向舍入 | 也称负无穷舍入,即舍入后结果小于原值 |
所以在此标准下,无法精确表示的非常大的整数将自动四舍五入。确切地说,JS 中的Number
类型只能安全地表示-9007199254740991 (-(2^53-1))
和9007199254740991(2^53-1)
之间的整数,任何超出此范围的整数值都可能失去精度。
console.log(9999999999999999); //10000000000000000
因为该整数大于JS Number类型所能表示的最大整数,所以它被四舍五入了,意外的四舍五入会损害程序的可靠性和安全性。例如:
// 注意最后一位的数字
9007199254740992 === 9007199254740993; //true
原因是因为,JS提供Number.MAX_SAFE_INTEGER
常量表示最大安全整数,Number.MIN_SAFE_INTEGER
常量表示最小安全整数。
可以把它打印出来:
const minInt = Number.MIN_SAFE_INTEGER;
console.log(minInt); //-9007199254740991
console.log(minInt - 5); //-9007199254740996
// 注意它是如何输出与上面相同的值的
console.log(minInt - 4); // -9007199254740996
解决方案
使用 bignumber.js
处理大整数
使用 BigInt
应用程序不再需要变通方法或库来安全地表示Number.MAX_SAFE_INTEGER
和Number.Min_SAFE_INTEGER
之外的整数。 现在可以在标准JS中执行对大整数的算术运算,而不会有精度损失的风险。
要创建BigInt
,只需在整数的末尾追加n即可。比如:
console.log(9007199254740995n); //9007199254740995n
console.log(9007199254740995); //9007199254740996
或者可以调用 BigInt()
构造函数
BigInt("9007199254740995"); //9007199254740995n
BigInt
构造函数
与其他基本类型一样,可以使用构造函数创建BigInt
。传递给BigInt()
的参数将自动转换为BigInt
BigInt("10"); //10n
BigInt(10); //10n
BigInt(true); //1n
无法转换的数据类型和值会引发异常
BigInt(10.2); //RangeError
BigInt(null); //TypeError
BigInt("abc"); //SyntaxError
可以直接对使用构造函数创建的BigInt执行算术操作
BigInt(10) * 10n; //100n
在使用严格相等运算符时,使用构造函数创建的BigInt
与常规BigInt
的处理方式类似
BigInt(true) === 1n; //true
console.log( 1n == true) //true
BigInt
也可以用二进制,八进制或十六进制表示
// binary 二进制
console.log(0b100000000000000000000000000000000000000000000000000011n);
//9007199254740995n
// hex 十六进制
console.log(0x20000000000003n);
//9007199254740995n
// octal 八进制
console.log(0o400000000000000003n);
//9007199254740995n
// 注意,不支持遗留的八进制语法
console.log(0400000000000000003n);
//SyntaxError 语法错误
不能使用严格相等运算符将BigInt
与常规数字进行比较,因为它们的类型不同, 如下:
console.log(1n === 1); //false
console.log(typeof 10n); //bigint
console.log(typeof 10); //number
但在使用等号运算符,它在处理操作之前执行隐式转换
console.log(1n == 1); //true
除了一元加号(+
)运算符外,所有的算术运算符都可以用于BigInt
1n + 20; //3n
1n - 2n; //-1n
+10n; //语法错误
//TypeError: Cannot convert a BigInt value
//to a number:不能将BigInt值转换为数字
-1n; //-1n
1n * 2n; //2n
2n / 2n; //1n
10n % 3n; //1n
10n ** 2n; //100n
var a = 1n;
++a; //2n
--a; //0n
不支持一元加号(+
)运算符的原因是某些程序可能依赖于+
始终生成Number
的不变量,或者抛出异常。 更改+的行为也会破坏asm.js
代码。
当然, BigInt
进行算术运算符的操作时返回的是 `BigInt值。因此,除法运算符的结果会自动向下舍入到最接近的整数。 如:
15 / 10; //1.5
15n / 10n //1n
隐式转换
因为隐式转换可能会导致信息丢失,所以不允许在 BigInt
和 Number
之间进行混合操作。当混合使用大整数和浮点数时,结果可能无法由 BigInt
或 Number
精确表示。如:
console.log((9007199254740992n + 1n) + 0.5)
//抛出错误:不能混合使用BigInt和其他类型,请使用显式转换
这个表达式的结果超出了BigInt
和Number
的范围。小数部分的Number
不能精确地转换为BigInt
。大于2^53
的BigInt
不能准确地转换为数字。
由于这个限制,不可能对混合使用Number
和BigInt
操作数执行算术操作。还不能将BigInt
传递给Web api和内置的 JS 函数,这些函数需要一个 Number
类型的数字。尝试这样做会报TypeError
类型错误
console.log(10 + 10n); //TypeError
console.log(Math.max(1n, 2n, 3n)); //TypeError
但是关系运算符不遵循改规则
console.log(2n > 1); //true
如果使用到BigInt和Number执行算术计算的话,首先要确定在哪个类型下进行操作
BigInt(5) + 10n; //15n
//或者
5 + Number(10n) //15
当BigInt
和 Boolean
类型一起使用时,方法和Number
类似
if(2n) {
//要执行的逻辑
}
如果判断是0n
时则无效
if(0n) {
//大括号里的逻辑将不会执行
}
在进行BigInts和Numbers数组的排序时,不会发生隐式类型转换
const arr = [3n, 4, 2, 1n, 0, -1n];
arr.sort(); //[-1n, 0, 1n, 2, 3n, 4]
显示转换和隐式转换
隐式转换就是系统默认
的、不需要加以声明就可以进行的转换。一般情况下,数据的类型的转换通常是由编译系统自动进行的,不需要人工干预,所以被称为隐式类型转换。但如果程序要求一定要将某一类型的数据转换为另外一种类型,则可以利用强制类型转换运算符
进行转换,这种强制转换过程称为显式转换。
总结
BigInt
是一种新的数据类型,用于当整数值大于Number
数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。
不能使用Number
和BigInt
操作数的混合执行算术运算,需要通过显式转换其中的一种类型。 此外,出于兼容性原因,不允许在BigInt
上使用一元加号(+
)运算符。