本文 AI 產出,尚未審核

JavaScript

單元:變數與資料型別(Variables & Data Types)

主題:BigInt


簡介

在日常開發中,我們大多使用 Number 來表示整數與浮點數,然而 JavaScript 的 Number 只能安全地表示到 ±2⁵³‑1(約 9 兆)之內的整數。當程式需要處理更大或更精確的整數時(例如金融計算、加密演算法、天文數據),Number 會產生精度損失,導致結果不可靠。

ECMAScript 2020 正式引入了 BigInt,讓開發者可以在 JavaScript 中直接操作任意長度的整數,且不會受到 53 位安全整數的限制。掌握 BigInt 的使用方式,能讓你的程式在處理大數、精密計算時更加穩定,也為未來的高精度需求打下基礎。

本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,完整介紹 BigInt 的使用方式,讓「初學者」也能快速上手,「中級開發者」能在專案中安全運用。


核心概念

1. 什麼是 BigInt

BigInt 是一種原始資料型別(primitive),用來表示 任意大小的整數。與 Number 不同,BigInt 可以超過 2⁵³‑1 的範圍,且在運算時保持 整數精度

注意BigInt 只能表示整數,不能直接與 Number 混用,除非先做型別轉換。

2. 建立 BigInt

有兩種常見的建立方式:

// 1. 在字面量後加上 n(最常見)
const big1 = 123456789012345678901234567890n;

// 2. 使用全域建構函式 BigInt()
const big2 = BigInt("123456789012345678901234567890");

// 3. 由 Number 轉換(僅在安全範圍內)
const big3 = BigInt(12345);   // 轉換結果為 12345n
  • 字面量 n 是最直觀且最易讀的寫法。
  • BigInt() 可接受字串或數字,適合從外部資料(例如 JSON、使用者輸入)建立大數。

3. 基本運算

BigInt 支援四則運算、比較運算與位元運算(除法會向下取整)。以下範例示範常見操作:

const a = 98765432109876543210n;
const b = 12345678901234567890n;

// 加法、減法、乘法
const sum = a + b;          // 111111111011111111100n
const diff = a - b;         // 86419753208641975320n
const prod = a * b;         // 1219326311370217952261850327336229230n

// 除法(向下取整)與餘數
const quot = a / b;         // 8n
const mod  = a % b;         // 9n

// 比較運算
console.log(a > b);         // true
console.log(a === 98765432109876543210n); // true

小技巧:如果需要精確的除法結果(包含小數),必須先將 BigInt 轉成 Number 或使用第三方函式庫,因為 BigInt 本身不支援小數。

4. 與 Number 的相互轉換

const big = 9007199254740993n;   // 超過 Number 安全上限
const num = Number(big);        // 9.007199254740993e+15(仍保留精度,但已是 Number)

// 從 Number 轉成 BigInt(僅在安全範圍內)
const safeNum = 123456;
const safeBig = BigInt(safeNum); // 123456n
  • BigIntNumber 可能會失去精度,僅在你確定結果仍在安全範圍時使用。
  • NumberBigInt 必須先確保原始 Number 為整數且在安全範圍內,否則會拋出 RangeError

5. BigInt 與 JSON

JSON.stringify() 會自動把 BigInt 轉成字串,否則會拋出 TypeError

const data = { id: 12345678901234567890n };
try {
  const json = JSON.stringify(data); // TypeError: Do not know how to serialize a BigInt
} catch (e) {
  console.error(e.message);
}

// 正確做法:先手動轉成字串
const safeJson = JSON.stringify({ id: data.id.toString() });
console.log(safeJson); // {"id":"12345678901234567890"}

在與後端或本地儲存交換資料時,建議將 BigInt 轉成字串,再在接收端使用 BigInt() 重新轉回。


程式碼範例

範例 1:計算階乘(Factorial)

/**
 * 使用 BigInt 計算任意正整數的階乘
 * @param {number|bigint} n
 * @returns {bigint}
 */
function factorial(n) {
  let result = 1n;
  // 允許傳入 Number,先轉成 BigInt
  let i = BigInt(n);
  for (; i > 1n; i--) {
    result *= i;
  }
  return result;
}

console.log(factorial(25));  // 15511210043330985984000000n

此函式即使計算 100! 這樣的極大數,也不會產生精度問題。

範例 2:大數加密雜湊(簡易示例)

/**
 * 將字串轉成對應的 BigInt,模擬簡易的雜湊演算法
 * @param {string} str
 * @returns {bigint}
 */
function simpleHash(str) {
  let hash = 0n;
  for (const ch of str) {
    // 取字元 Unicode 編碼,累加後乘上一個大質數
    hash = (hash + BigInt(ch.codePointAt(0))) * 31n;
  }
  // 取模以限制長度(此處使用 2^64 為例)
  return hash % (2n ** 64n);
}

console.log(simpleHash("BigInt 範例")); // 1234567890123456789n(示意值)

此例說明 BigInt 在加密、雜湊等需要大數運算的領域 的可行性。

範例 3:處理金融金額(避免精度錯誤)

// 假設每筆交易金額以「分」為單位,使用 BigInt 保障精度
const transactions = [
  1999n,   // $19.99
  4500n,   // $45.00
  12345678901234567890n // 超大金額
];

function totalAmount(list) {
  return list.reduce((sum, v) => sum + v, 0n);
}

const totalCents = totalAmount(transactions);
console.log(`總金額:${totalCents} 分 (${Number(totalCents) / 100} 元)`);
// 若金額過大,Number(totalCents) 可能失去精度,建議直接使用字串輸出
console.log(`總金額(字串):${totalCents.toString()} 分`);

透過 BigInt 儲存最小貨幣單位(分或厘),可以避免浮點數四捨五入的問題。

範例 4:與 Date 結合計算 Unix 時間戳(毫秒)

// 取得目前時間的毫秒時間戳(Number),轉成 BigInt 以做後續大數運算
const nowMs = BigInt(Date.now());

// 假設要計算 10 年後的時間戳
const tenYearsMs = nowMs + 10n * 365n * 24n * 60n * 60n * 1000n;
console.log(new Date(Number(tenYearsMs)).toISOString());
// 輸出:2034-11-19T...(依實際執行時間而定)

在需要 跨越極長時間範圍(例如天文觀測、歷史資料)時,使用 BigInt 可避免毫秒級溢位。


常見陷阱與最佳實踐

陷阱 說明 解決方式
NumberBigInt 直接比較或運算 例如 1n + 1 會拋出 TypeError 必須先將雙方轉成相同型別:Number(1n) + 11n + BigInt(1)
除法結果被截斷 BigInt 的除法只返回整數部份(向下取整)。 若需要小數,先轉成 Number 或使用外部高精度函式庫(如 decimal.js)。
JSON 序列化失敗 JSON.stringify({v: 10n}) 直接拋錯。 手動 toString() 後再序列化,或使用自訂 replacer。
意外的隱式型別轉換 == 會嘗試把 BigInt 轉成 Number,可能產生精度問題。 建議使用嚴格相等 ===,或明確轉型。
性能考量 BigInt 計算較 Number 慢,特別是大量迭代時。 僅在需要大數或精度保證時使用,否則保持 Number

最佳實踐

  1. 明確宣告型別:使用 n 後綴或 BigInt() 建立,避免混用。
  2. 封裝轉型邏輯:在函式入口處統一把參數轉成 BigInt,減少重複程式碼。
  3. 避免在大量迴圈中頻繁轉型:一次性轉換後再進行運算。
  4. 使用字串儲存:與外部系統交換大數時,統一使用字串格式。
  5. 測試邊界值:特別是與 Number 交互的情況,寫單元測試驗證精度。

實際應用場景

  1. 金融系統:交易金額、利息計算、加密貨幣區塊高度等,需要精確到最小單位且可能超過 Number 安全範圍。
  2. 區塊鏈與加密:區塊高度、哈希值、簽名運算常以 256 位或更長的整數表示,BigInt 為天然匹配。
  3. 科學計算:天文學、基因組學等領域的計數常達到 10⁹⁰ 以上,BigInt 可避免溢位。
  4. 大型統計與計數:網站訪問量、社交平台粉絲數等,未來可能突破 Number 上限。
  5. 時間序列:毫秒或納秒級時間戳累加,跨越多年仍保持正確。

在上述情境中,將資料模型設計為 BigInt(或字串+BigInt,能確保系統在面對極端數值時不會出現不預期的錯誤或精度損失。


總結

BigInt 為 JavaScript 提供了 任意精度的整數 支援,解決了 Number 在大數運算上的限制。透過字面量 nBigInt() 建構子,我們可以輕鬆建立、比較與運算巨大的整數;同時,了解 型別相容性、JSON 序列化與性能成本,是安全使用 BigInt 的關鍵。

在金融、加密、科學與大數統計等領域,BigInt 已成為不可或缺的工具。只要遵循「明確型別、避免混用、適時轉型」的最佳實踐,開發者即可在日常 JavaScript 專案中,穩定且高效地處理任何大小的整數。

實務建議:在新專案的資料模型設計階段,就先評估是否需要 BigInt,若確定會處理超過安全範圍的整數,立即採用 BigInt(或字串+BigInt)作為底層儲存型別,避免未來因精度問題而重構程式碼。

祝你在 JavaScript 的世界裡,玩轉大數無阻! 🚀