Back

什么时候你可能需要在 JavaScript 中使用 BigInt?

什么时候你可能需要在 JavaScript 中使用 BigInt?

JavaScript 对大多数数字的处理都很好——直到它不行的时候。如果你曾经从 API 获取一个大型 ID 并注意到它返回时略有错误,或者尝试对一个非常大的整数进行精确运算却得到了一个微妙的错误结果,那么你已经遇到了 BigInt 所解决的问题。

核心要点

  • JavaScript 数字使用 IEEE 754 双精度格式,这将安全整数限制在 −(2⁵³ − 1) 到 2⁵³ − 1 的范围内。超出该范围,值会悄无声息地失去精度。
  • BigInt 是一种原始类型(在 ES2020 中引入),可以表示任意大小的整数,但它不能处理小数。
  • 实际应用场景包括处理大型外部 ID(例如 Twitter Snowflake ID)、64 位 WebAssembly 整数,以及大型计数器或区块链数据的精确运算。
  • BigInt 不能在表达式中与 Number 混合使用,不能与 Math API 一起使用,并且需要对 JSON 序列化进行自定义处理。

JavaScript 数字无法跨越的界限

所有 JavaScript 数字都使用 IEEE 754 双精度浮点格式。这为你提供了一个安全整数范围:−(2⁵³ − 1) 到 2⁵³ − 1,由 Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGER 表示。

超出该边界,整数会悄无声息地失去精度:

console.log(9007199254740991 + 1)  // 9007199254740992 ✅
console.log(9007199254740991 + 2)  // 9007199254740992 ❌ (错误)

// 这就是精度问题的实际表现:
9007199254740992 === 9007199254740993 // true — 对 JavaScript 来说它们是相同的

不会抛出错误。值就是错的。这就是 BigInt 旨在解决的精度限制问题。

BigInt 到底是什么

BigInt 是 ES2020 中引入的内置 JavaScript 原始类型,可以表示任意大小的整数,仅受可用内存限制。你可以通过在整数字面量后附加 n 或使用 BigInt() 构造函数来创建一个:

const big = 9007199254740993n                 // 字面量语法
const alsoBig = BigInt("9007199254740993")    // 使用字符串的构造函数

9007199254740992n === 9007199254740993n        // false ✅ — 正确

一个重要的约束:BigInt 仅限于整数。它不能表示小数或分数值。1.5n 会抛出 SyntaxError

何时在 JavaScript 中使用 BigInt

大多数前端代码从不需要 BigInt。但有几个实际场景确实需要它。

来自外部系统的大型 ID。 Twitter 的 Snowflake ID 系统和类似的分布式 ID 方案会生成超过 Number.MAX_SAFE_INTEGER 的 64 位整数。当 REST API 将其中一个作为 JSON 数字返回时,JavaScript 会悄无声息地破坏它。将其解析为字符串并转换为 BigInt 可以完全保留该值。

// 从 API 接收的作为字符串的 64 位 ID
const userId = BigInt("922337203685477580")

来自 WebAssembly 的 64 位整数。 WebAssembly 的 i64 类型直接映射到 JavaScript 中的 BigInt。如果你的 Wasm 模块返回或接受 64 位整数,你将需要 BigInt 来正确处理它们。

精度绝不能出错的精确整数运算。 某些区块链交易值、大型计数器或高精度时间戳运算(例如,自纪元以来的纳秒)可能超出安全整数范围。BigInt 保持每一位数字的精确性。

需要了解的重要限制

在使用 BigInt 之前,请了解其限制:

你不能在算术运算中混合使用 BigInt 和 Number。 这样做会抛出 TypeError。需要显式转换:

const big = 10n
const num = 5

big + num            // ❌ TypeError
big + BigInt(num)    // ✅ 15n
Number(big) + num    // ✅ 15 (但对于大值会失去 BigInt 精度)

Math API 不能与 BigInt 一起使用。 Math.max()Math.sqrt() 和所有其他 Math 方法如果传递 BigInt 都会抛出错误。如果你需要这些操作,你需要进行转换——并接受精度权衡。

JSON 序列化默认失败。 JSON.stringify() 在 BigInt 值上会抛出 TypeError。你需要自定义替换器或 toJSON() 方法来处理序列化:

BigInt.prototype.toJSON = function () {
  return this.toString()
}

JSON.stringify({ id: 922337203685477580n }) // '{"id":"922337203685477580"}'

请注意,这会将 JSON 输出中的值转换为字符串,接收系统需要相应地处理。此外,在生产代码中通常不鼓励修改像 BigInt.prototype 这样的内置原型。传递给 JSON.stringify() 的自定义 replacer 函数是更安全的替代方案:

const data = { id: 922337203685477580n }

JSON.stringify(data, (key, value) =>
  typeof value === "bigint" ? value.toString() : value
) // '{"id":"922337203685477580"}'

总结

JavaScript 中的 BigInt 与 Number 并不是真正的竞争关系——它们服务于不同的目的。对于 Number 处理得很好的所有事情(也就是大多数事情)使用 Number。特别是当你处理可能超过 2⁵³ − 1 的整数且正确性不容商量时,才使用 BigInt。这是一个狭窄但重要的类别,而 BigInt 能够完美地处理它。

常见问题

BigInt 在所有现代浏览器中都受支持,包括 Chrome、Firefox、Safari 和 Edge。但是,Internet Explorer 不支持它。如果你需要支持 IE,则需要将值保留为字符串或使用大数字库,而不是依赖原生 BigInt。

可以。你可以使用标准比较运算符(如小于、大于和三等号)将 BigInt 值与其他 BigInt 进行比较。你还可以使用宽松相等来比较 BigInt 和 Number,因此 10n == 10 返回 true,但 10n === 10 返回 false,因为它们是不同的类型。

有。BigInt 操作通常比 Number 操作慢,因为它们使用任意精度算术而不是固定大小的硬件指令。对于大多数应用程序,差异可以忽略不计,但在性能关键的循环或热路径中,你应该进行基准测试,并在值保持在安全整数范围内时优先使用 Number。

使用 Number 构造函数,如 Number(100n),它返回 100。但要小心大值。如果 BigInt 超过 Number.MAX_SAFE_INTEGER,转换将悄无声息地失去精度。在转换之前,始终检查值是否在安全范围内。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay