BigInt 解决了什么问题?使用时有哪些限制?
简化版
BigInt 用来表示超过 Number.MAX_SAFE_INTEGER 的整数,避免大整数精度丢失;它通过 123n 或 BigInt() 创建,但不能和普通 Number 直接混算,也不能表示小数。
详细版
JavaScript 的 Number 使用双精度浮点数,安全整数范围是 -(2^53 - 1) 到 2^53 - 1。
超过这个范围后,整数运算可能不再精确。
Number.MAX_SAFE_INTEGER; // 9007199254740991
9007199254740991 + 1; // 9007199254740992
9007199254740991 + 2; // 9007199254740992
BigInt 可以表达任意大的整数:
const id = 9007199254740993n;
面试重点:它解决大整数精度,不解决小数精度;不能和 Number 随意混算;JSON 序列化要特别处理。
完整版教学
一、Number 的安全整数边界从哪里来
JavaScript 的普通数字是 IEEE 754 双精度浮点数。
它能表示很大的范围,但不是每个整数都能精确表示。
安全整数上限是 2^53 - 1,也就是 9007199254740991。
MAX_SAFE_INTEGER = 2^53 - 1
= 9007199254740991
超过这个范围后,相邻整数可能映射到同一个 Number。
这就是为什么 9007199254740991 + 1 和 + 2 可能得到相同结果。
对于订单号、雪花 ID、数据库长整型主键,这会造成严重问题。
二、BigInt 专门表示任意精度整数
BigInt 用 n 后缀或 BigInt() 创建。
它不是 Number 的增强模式,而是一个新的原始类型。
typeof 1n 的结果是 "bigint"。
const a = 123n;
const b = BigInt('9007199254740993');
console.log(typeof a); // "bigint"
console.log(b + 1n); // 9007199254740994n
如果有 19 位订单号,用 Number 接收可能丢精度。 用字符串传输、进入 JS 后按需转成 BigInt,通常更稳妥。
三、BigInt 不能和 Number 直接混合运算
BigInt 和 Number 表达的是不同数字系统。 直接混合加减乘除会抛出 TypeError。 这样设计是为了避免隐式转换带来的精度损失。
1n + 2; // TypeError
1n + BigInt(2); // 3n
Number(1n) + 2; // 3
| 操作 | 结果 | 风险 |
|---|---|---|
1n + 2 | 报错 | 禁止隐式混算 |
1n + 2n | 3n | 保持整数精度 |
Number(big) | Number | 可能丢精度 |
BigInt(num) | BigInt | num 不能是小数 |
转换方向要看业务。 如果值可能超过安全整数,应尽量保留 BigInt 或字符串,不要转回 Number。
四、BigInt 只能表示整数,不能处理小数精度
BigInt 不是金融小数方案。
它不能表示 1.5n,也不能直接处理 0.1 + 0.2 的浮点误差。
如果要处理金额,常见做法是用整数分单位或使用十进制定点库。
// 错误
// const x = 1.5n;
const cents = 1999n; // 19.99 元用分表示
数字例子:19.99 元可以存成 1999 分。
用 BigInt 可以让特别大的金额整数部分保持精度,但小数位规则仍要由业务定义。
五、JSON 和接口传输要特别注意
JSON.stringify 默认不能直接序列化 BigInt。
这在前后端接口、日志上报、本地缓存中都很常见。
通常会把 BigInt 转成字符串传输。
const data = { id: 9007199254740993n };
JSON.stringify(data); // TypeError
JSON.stringify(data, (_, value) =>
typeof value === 'bigint' ? value.toString() : value
);
流程建议:
后端 long / bigint
-> JSON 中用字符串
-> 前端展示直接用字符串
-> 需要整数运算时转 BigInt
这能避免进入 JS Number 后已经丢精度。
六、比较、排序和工程边界
BigInt 可以和 Number 做宽松相等或大小比较,但不建议依赖复杂隐式规则。
排序时尤其要小心,不要写 a - b,因为减法会混合运算。
更稳妥的写法是返回 -1/0/1。
const list = [3n, 1n, 2n];
list.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
记忆钩子:BigInt 是大整数保险箱,不是所有数字问题的万能药。
工程中最常见的建议是:ID 类字段不要用 Number 接。 展示用字符串,计算用 BigInt,涉及小数用定点方案。
七、常见误区与追问
- 误区:BigInt 可以解决
0.1 + 0.2问题。 BigInt 只能表示整数,小数精度要用定点或十进制库处理。 - 误区:后端 long 到前端直接用 Number 没问题。 超过安全整数后可能丢精度,ID 应优先按字符串传输。
- 误区:BigInt 能和 Number 自动相加。 直接混合运算会报错,需要显式转换。
- 追问:为什么 BigInt 不能直接 JSON 序列化? JSON 标准没有 BigInt 数字类型,默认序列化会抛错。
- 追问:
Number.MAX_SAFE_INTEGER是多少? 是9007199254740991,也就是2^53 - 1。 - 追问:大整数排序为什么不能写
a - b? 如果其中是 BigInt,减法结果也是 BigInt 或触发混算问题,排序函数应返回普通 Number 的比较结果。
八、加强记忆
BigInt 要抓住三个边界:它解决超过 2^53 - 1 的整数精度问题,不解决小数问题,不和 Number 隐式混算。前端遇到大 ID 时优先字符串传输和展示,确实要整数计算再转 BigInt。