乐趣区

关于javascript:JS最新基本数据类型BigInt

BigInt数据类型的目标是比 Number 数据类型反对的范畴更大的整数值。在对大整数执行数学运算时,以任意精度示意整数的能力尤为重要。应用BigInt,整数溢出将不再是问题。

此外,能够平安地应用更加精确工夫戳,大整数 ID 等,而无需应用变通方法。BigInt目前是第 3 阶段提案,一旦增加到标准中,它就是 JS 第二个数字数据类型,也将是 JS 第 8 种根本数据类型:

  • Boolean
  • Null
  • Undefined
  • Number
  • BigInt
  • String
  • Symbol
  • Object

在本文中,咱们将具体介绍 BigInt,看看它如何解决应用Number 类型的限度。

问题

对于学过其余语言的程序员来说,JS 中短少显式整数类型经常令人困惑。许多编程语言反对多种数字类型,如浮点型、双精度型、整数型和双精度型,但 JS 却不是这样。在 JS 中,依照 IEEE 754-2008 规范的定义,所有数字都以双精度 64 位浮点格局示意。

在此规范下,无奈准确示意的十分大的整数将主动四舍五入。确切地说,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

// notice how this outputs the same value as above
console.log(minInt - 4);     // → -9007199254740996 

解决方案

为了解决这些限度,一些 JS 开发人员应用字符串类型示意大整数。例如,Twitter API 在应用 JSON 进行响应时会向对象增加字符串版本的 ID。此外,还开发了许多库,例如 bignumber.js,以便更容易地解决大整数。

应用 BigInt,应用程序不再须要变通方法或库来平安地示意 Number.MAX_SAFE_INTEGERNumber.Min_SAFE_INTEGER之外的整数。当初能够在规范 JS 中执行对大整数的算术运算,而不会有精度损失的危险。

要创立BigInt,只需在整数的开端追加 n 即可。比拟:

console.log(9007199254740995n);    // → 9007199254740995n
console.log(9007199254740995);     // → 9007199254740996 

或者,能够调用 BigInt() 构造函数

BigInt("9007199254740995");    // → 9007199254740995n 

BigInt文字也能够用二进制、八进制或十六进制示意

// binary
console.log(0b100000000000000000000000000000000000000000000000000011n);
// → 9007199254740995n

// hex
console.log(0x20000000000003n);
// → 9007199254740995n

// octal
console.log(0o400000000000000003n);
// → 9007199254740995n

// note that legacy octal syntax is not supported
console.log(0400000000000000003n);
// → SyntaxError 

请记住,不能应用严格相等运算符将 BigInt 与惯例数字进行比拟,因为它们的类型不同:

console.log(10n === 10);    // → false

console.log(typeof 10n);    // → bigint
console.log(typeof 10);     // → number 

相同,能够应用等号运算符,它在解决操作数之前执行隐式类型转换

console.log(10n == 10);    // → true 

除一元加号 (+) 运算符外,所有算术运算符都可用于BigInt

10n + 20n;    // → 30n
10n - 20n;    // → -10n
+10n;         // → TypeError: Cannot convert a BigInt value to a number
-10n;         // → -10n
10n * 20n;    // → 200n
20n / 10n;    // → 2n
23n % 10n;    // → 3n
10n ** 3n;    // → 1000n

const x = 10n;
++x;          // → 11n
--x;          // → 9n 

不反对一元加号(+)运算符的起因是某些程序可能依赖于 + 始终生成 Number 的不变量,或者抛出异样。更改 + 的行为也会毁坏 asm.js 代码。

当然,与 BigInt 操作数一起应用时,算术运算符应该返回 BigInt 值。因而,除法 (/) 运算符的后果会主动向下舍入到最靠近的整数。例如:

25 / 10;      // → 2.5
25n / 10n;    // → 2n 

隐式类型转换

因为隐式类型转换可能失落信息,所以不容许在 bigintNumber 之间进行混合操作。当混合应用大整数和浮点数时,后果值可能无奈由 BigIntNumber准确示意。思考上面的例子:

(9007199254740992n + 1n) + 0.5 

这个表达式的后果超出了 BigIntNumber的范畴。小数局部的 Number 不能准确地转换为 BigInt。大于2^53BigInt不能精确地转换为数字。

因为这个限度,不可能对混合应用 NumberBigInt操作数执行算术操作。还不能将 BigInt 传递给 Web api 和内置的 JS 函数,这些函数须要一个 Number 类型的数字。尝试这样做会报 TypeError 谬误

10 + 10n;    // → TypeError
Math.max(2n, 4n, 6n);    // → TypeError 

请留神,关系运算符不遵循此规定,如下例所示:

10n > 5;    // → true 

如果心愿应用 BigIntNumber执行算术计算,首先须要确定应该在哪个类型中执行该操作。为此,只需通过调用 Number()BigInt()来转换操作数:

BigInt(10) + 10n;    // → 20n
// or
10 + Number(10n);    // → 20 

Boolean 类型与 BigInt 类型相遇时,BigInt 的解决形式与 Number 相似,换句话说,只有不是 0nBigInt 就被视为 truthy 的值:

if (5n) {// 这里代码块将被执行}

if (0n) {// 这里代码块不会执行} 

排序 BigIntsNumbers数组时,不会产生隐式类型转换:

const arr = [3n, 4, 2, 1n, 0, -1n];

arr.sort();    // → [-1n, 0, 1n, 2, 3n, 4] 

位操作符如 |、&、<<、>>^Bigint 的操作形式与 Number 相似。上面是一些例子

90 | 115;      // → 123
90n | 115n;    // → 123n
90n | 115;     // → TypeError 

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 

库函数

在撰写本文时,Chrome +67Opera +54 齐全反对 BigInt 数据类型。可怜的是,EdgeSafari 还没有实现它。Firefox默认不反对 BigInt,然而能够在 about:config 中将 javascript.options.bigint 设置为true 来开启它,最新反对的状况可在“Can I use”上查看。

可怜的是,转换 BigInt 是一个极其简单的过程,这会导致重大的运行时性能损失。间接 polyfill BigInt也是不可能的,因为该提议扭转了几个现有操作符的行为。目前,更好的抉择是应用 JSBI 库,它是 BigInt 提案的纯 JS 实现。

这个库提供了一个与原生 BigInt 行为完全相同的 API。上面是如何应用 JSBI:

import JSBI from './jsbi.mjs';

const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
const b2 = JSBI.BigInt('10');

const result = JSBI.add(b1, b2);

console.log(String(result));    // → '9007199254741001' 

应用 JSBI 的一个长处是,一旦浏览器反对,就不须要重写代码。相同,能够应用 babel 插件主动将 JSBI 代码编译为原生 BigInt代码。

总结

BigInt是一种新的数据类型,用于当整数值大于 Number 数据类型反对的范畴时。这种数据类型容许咱们平安地对大整数执行算术操作,示意高分辨率的工夫戳,应用大整数 id,等等,而不须要应用库。

重要的是要记住,不能应用 NumberBigInt操作数的混合执行算术运算,须要通过显式转换其中的一种类型。此外,出于兼容性起因,不容许在 BigInt 上应用一元加号(+)运算符。

退出移动版