JavaScript 最新數據類型的基本指南:BigInt
已發表: 2022-03-10Number
類型不能安全地表示大於 2 53的整數值。 這種限制迫使開發人員使用低效的變通方法和第三方庫。 BigInt
是一種新的數據類型,旨在解決這個問題。 BigInt
數據類型旨在使 JavaScript 程序員能夠表示大於Number
數據類型支持的範圍的整數值。 在對大整數執行數學運算時,以任意精度表示整數的能力尤為重要。 使用BigInt
,整數溢出將不再是問題。
此外,您可以安全地使用高分辨率時間戳、大整數 ID 等,而無需使用解決方法。 BigInt
目前是第 3 階段的提案。 一旦添加到規範中,它將成為 JavaScript 中的第二種數字數據類型,這將使支持的數據類型總數達到八種:
- 布爾值
- 空值
- 不明確的
- 數字
- 大整數
- 細繩
- 象徵
- 目的
在本文中,我們將仔細研究BigInt
,看看它如何幫助克服 JavaScript 中Number
類型的限制。
問題
JavaScript 中缺乏明確的整數類型常常讓來自其他語言的程序員感到困惑。 許多編程語言支持多種數字類型,例如 float、double、integer 和 bignum,但 JavaScript 並非如此。 在 JavaScript 中,所有數字都以 IEEE 754-2008 標准定義的雙精度 64 位浮點格式表示。
在此標準下,無法精確表示的非常大的整數會自動四捨五入。 準確地說,JavaScript 中的Number
類型只能安全地表示介於 -9007199254740991 (-(2 53 -1)) 和 9007199254740991 (2 53 -1) 之間的整數。 任何超出此範圍的整數值都可能會丟失精度。
這可以通過執行以下代碼輕鬆檢查:
console.log(9999999999999999); // → 10000000000000000
這個整數大於 JavaScript 可以用Number
原語可靠表示的最大數字。 因此,它是圓形的。 意外的捨入可能會損害程序的可靠性和安全性。 這是另一個例子:
// notice the last digits 9007199254740992 === 9007199254740993; // → true
JavaScript 提供了Number.MAX_SAFE_INTEGER
常量,可以讓您快速獲取 JavaScript 中的最大安全整數。 同樣,您可以使用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
解決方案
作為解決這些限制的方法,一些 JavaScript 開發人員使用String
類型表示大整數。 例如,Twitter API 在使用 JSON 響應時將字符串版本的 ID 添加到對象。 此外,還開發了許多庫,例如 bignumber.js,以使處理大整數更容易。
使用BigInt
,應用程序不再需要解決方法或庫來安全地表示Number.MAX_SAFE_INTEGER
和Number.Min_SAFE_INTEGER
之外的整數。 現在可以在標準 JavaScript 中執行對大整數的算術運算,而不會損失精度。 與第三方庫相比,使用本機數據類型的額外好處是更好的運行時性能。
要創建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 let x = 10n; ++x; // → 11n --x; // → 10n
不支持一元加號 ( +
) 運算符的原因是某些程序可能依賴於+
始終生成Number
或拋出異常的不變量。 更改+
的行為也會破壞 asm.js 代碼。

自然,當與BigInt
操作數一起使用時,算術運算符應返回BigInt
值。 因此,除法 ( /
) 運算符的結果會被自動截斷。 例如:
25 / 10; // → 2.5 25n / 10n; // → 2n
隱式類型轉換
因為隱式類型轉換可能會丟失信息,所以BigInt
和Number
之間的混合操作是不允許的。 混合大整數和浮點數時,結果值可能無法用BigInt
或Number
準確表示。 考慮以下示例:
(9007199254740992n + 1n) + 0.5
此表達式的結果在BigInt
和Number
的域之外。 帶有小數部分的Number
不能準確地轉換為BigInt
。 大於 2 53的BigInt
無法準確轉換為Number
。
由於此限制,無法使用Number
和BigInt
操作數的混合執行算術運算。 您也不能將BigInt
傳遞給需要Number
的 Web API 和內置 JavaScript 函數。 嘗試這樣做會導致TypeError
:
10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError
請注意,關係運算符不遵循此規則,如下例所示:
10n > 5; // → true
如果您想使用BigInt
和Number
執行算術計算,您首先需要確定應該在其中完成操作的域。 為此,只需調用Number()
或BigInt()
轉換任一操作數:
BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20
在Boolean
上下文中遇到時, BigInt
的處理方式與Number
類似。 換句話說,只要不是0n
, BigInt
就被認為是真實值:
if (5n) { // this code block will be executed } if (0n) { // but this code block won't }
對數組進行排序時, BigInt
和Number
類型之間不會發生隱式類型轉換:
const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]
位運算符,例如|
、 &
、 <<
、 >>
和^
以與Number
類似的方式對BigInt
進行操作。 負數被解釋為無限長的二進制補碼。 不允許混合操作數。 這裡有些例子:
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(true) === 1n; // → true
庫函數
JavaScript 提供了兩個庫函數用於將BigInt
值表示為有符號或無符號整數:
-
BigInt.asUintN(width, BigInt)
:將BigInt
包裝在 0 到 2寬度-1 之間 BigInt.asIntN(width, BigInt)
:在 -2 width-1和 2 width-1 -1 之間包裝BigInt
這些函數在執行 64 位算術運算時特別有用。 這樣您就可以保持在預期範圍內。
瀏覽器支持和轉譯
在撰寫本文時,Chrome +67 和 Opera +54 完全支持BigInt
數據類型。 不幸的是,Edge 和 Safari 還沒有實現它。 Firefox 默認不支持BigInt
,但可以通過在about:config
中將javascript.options.bigint
設置為true
來啟用它。 Can I use... 上提供了支持的瀏覽器的最新列表。
不幸的是,轉譯BigInt
是一個極其複雜的過程,會導致嚴重的運行時性能損失。 直接 polyfill BigInt
也是不可能的,因為該提案改變了幾個現有運算符的行為。 目前,更好的選擇是使用 JSBI 庫,它是BigInt
提案的純 JavaScript 實現。
這個庫提供了一個行為與原生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
代碼。 此外,JSBI 的性能與原生BigInt
實現相當。 您可以期待BigInt
的更廣泛的瀏覽器支持。
結論
BigInt
是一種新的數據類型,用於在整數值大於Number
數據類型支持的範圍時使用。 這種數據類型允許我們安全地對大整數執行算術運算、表示高分辨率時間戳、使用大整數 ID 等等,而無需使用庫。
重要的是要記住,不能同時使用Number
和BigInt
操作數來執行算術運算。 您需要通過顯式轉換任一操作數來確定應在其中完成操作的域。 此外,出於兼容性原因,不允許在BigInt
上使用一元加號 ( +
) 運算符。
你怎麼看? 你覺得BigInt
有用嗎? 讓我們在評論中知道!