为什么 JavaScript 需要 BigInt
JavaScript 的 number 类型是基于 IEEE 754 双精度浮点数,尾数位只有 53 位。这意味着它能精确表示的整数范围是 -(2^53 - 1) 到 2^53 - 1,即 -9007199254740991 到 9007199254740991。
这个范围对于日常计算足够用,但一旦涉及以下场景,就会遇到精度问题:
- 加密计算:RSA 算法常用 2048 位甚至 4096 位的整数
- 金融计算:货币计算需要精确到小数点后多位
- 时间戳:纳秒级时间需要更大的整数范围
- ID 生成:分布式系统生成的 ID 可能超过 53 位
BigInt 是 ES2020 引入的新类型,专门用于处理任意大小的整数。
一、BigInt 的基础
1.1 创建 BigInt
// 字面量语法:在整数后加 n
const big1 = 123n;
// BigInt() 构造函数
const big2 = BigInt(123); // 123n
// 从字符串解析
const big3 = BigInt('123456789012345678901234567890');
const big4 = BigInt('0x1FFFFFFFFFFFFFFF'); // 十六进制
// 从其他 BigInt 转换
const big5 = BigInt(big1);
1.2 BigInt 与 Number 的区别
| 特性 | number | bigint |
|---|---|---|
| 类型标签 | 1 (对象) | 2 (在 typeof 中仍是 'symbol',但有特殊内部标记) |
| 精度 | 53 位尾数 | 任意精度 |
| 最大安全整数 | Number.MAX_SAFE_INTEGER (2^53 - 1) | 无限制 |
| 浮点支持 | 是 | 否(只能是整数) |
| 与 JSON 序列化 | 自动 | 需要自定义序列化 |
// 精度问题示例
const num = 9007199254740993;
console.log(num); // 9007199254740992 — 精度丢失
// BigInt 精确表示
const big = 9007199254740993n;
console.log(big); // 9007199254740993n — 精确
1.3 typeof 的特殊性
typeof 123n; // 'bigint' (ES2020+)
BigInt.prototype.toString; // 方法存在
二、BigInt 运算
2.1 算术运算
BigInt 支持所有标准算术运算符:
const a = 10n;
const b = 3n;
// 加法
console.log(a + b); // 13n
// 减法
console.log(a - b); // 7n
// 乘法
console.log(a * b); // 30n
// 除法(向下取整)
console.log(a / b); // 3n (不是 3.333...)
console.log(10n / 3n); // 3n
// 取模
console.log(a % b); // 1n
// 幂运算
console.log(2n ** 100n); // 巨大的数字
2.2 位运算
BigInt 也支持位运算:
const x = 0xFFFFFFn;
// 按位与
console.log(x & 0xFFn); // 255n
// 按位或
console.log(x | 0xFFn); // 16777215n
// 按位异或
console.log(x ^ 0xFFn); // 16776960n
// 按位取反
console.log(~x); // -16777216n
// 左移
console.log(1n << 100n); // 1267650600228229401496703205376n
// 右移(算术右移,保留符号位)
console.log(-16n >> 2n); // -4n
// 无符号右移(无符号右移,负数会变成大正数)
console.log(-16n >>> 2n); // 4611686018427387900n
2.3 比较运算
// BigInt 与 BigInt 比较
console.log(10n > 5n); // true
console.log(10n < 5n); // false
console.log(10n === 10n); // true
// BigInt 与 Number 比较(类型不同,永远不等)
console.log(10n === 10); // false
// BigInt 与 Number 可以比较
console.log(10n > 5); // true(BigInt 自动转换)
console.log(10n > 5.9); // true(5.9 转为 5)
// 混合运算会抛出错误
try {
10n + 5;
} catch (e) {
console.log(e.message); // "Cannot mix BigInt and other types"
}
2.4 隐式类型转换
BigInt 不会自动转换为 number,也不会自动从 number 转换。这与其他语言不同,但避免了精度问题:
// 不支持隐式转换
10n + 5; // TypeError
10n + Number(5); // 15n — 需要显式转换
// Boolean 转换支持
Boolean(0n); // false
Boolean(1n); // true
if (0n) { } // 条件为 false
三、BigInt 的限制
3.1 不能表示小数
// 错误:BigInt 只能是整数
// BigInt(3.14); // SyntaxError
// 需要小数时,使用 Number 或 Decimal 库
const precise = 314n / 100n; // 3n(整数除法)
3.2 不支持某些内置操作
// 不支持 Math 方法
Math.abs(-10n); // TypeError
// 不支持 JSON.stringify 自动序列化
const obj = { value: 10n };
JSON.stringify(obj); // '{"value": "无法序列化"}'
// 解决方案:自定义序列化
JSON.stringify(obj, (k, v) => typeof v === 'bigint' ? v.toString() : v);
3.3 不能与 number 混合运算
// 所有混合运算都需要显式转换
const result = 10n + BigInt(5); // 15n
const result2 = 10n + Number(5); // TypeError
const result3 = Number(10n) + 5; // 15
四、实用场景
4.1 大整数计数
class BigCounter {
#value = 0n;
get value() {
return this.#value;
}
increment() {
this.#value++;
}
incrementBy(n) {
this.#value += BigInt(n);
}
}
4.2 加密计算基础
// 简化的 RSA 加密演示
function modPow(base, exponent, modulus) {
if (modulus === 1n) return 0n;
let result = 1n;
base = base % modulus;
while (exponent > 0n) {
if (exponent % 2n === 1n) {
result = (result * base) % modulus;
}
exponent = exponent / 2n;
base = (base * base) % modulus;
}
return result;
}
// RSA 加密/解密基础
const p = 61n;
const q = 53n;
const n = p * q; // 3233n
const phi = (p - 1n) * (q - 1n); // 3120n
const e = 17n; // 公钥指数
const d = modPow(e, 1n, phi); // 2753n — 私钥指数
4.3 精确金融计算
// 使用最小单位进行计算(分、厘等)
const DOLLAR = 100n;
const CENT = 1n;
class Money {
#cents;
constructor(dollars, cents = 0n) {
this.#cents = BigInt(dollars) * DOLLAR + BigInt(cents);
}
add(other) {
return new Money(0, this.#cents + other.#cents);
}
subtract(other) {
return new Money(0, this.#cents - other.#cents);
}
toString() {
const dollars = this.#cents / DOLLAR;
const cents = this.#cents % DOLLAR;
return `$${dollars}.${cents.toString().padStart(2, '0')}`;
}
}
const price1 = new Money(10, 99n);
const price2 = new Money(5, 50n);
console.log(price1.add(price2).toString()); // $16.49
五、BigInt 的性能
5.1 性能特点
BigInt 的性能与整数大小相关:
- 小整数(< 64 位):性能接近 number
- 大整数:性能明显下降,位运算时间与位数成正比
5.2 何时使用 BigInt
应该使用 BigInt:
- 整数可能超过 Number.MAX_SAFE_INTEGER
- 需要精确整数运算(如货币)
- 加密/数学计算
不应使用 BigInt:
- 普通整数计算(使用 number 更高效)
- 需要浮点数精度(BigInt 不支持小数)
六、面试高频考点
考点 1:BigInt 与 Number 的区别
- number 是 IEEE 754 双精度浮点,53 位精度
- BigInt 是任意精度整数,无大小限制
- BigInt 不能与 number 混合运算
- BigInt 不支持小数
考点 2:精度问题
number 的精度限制导致大整数运算出现误差:
console.log(9007199254740992 + 1); // 9007199254740992(丢失精度)
console.log(9007199254740992n + 1n); // 9007199254740993n(正确)
考点 3:BigInt 的限制
BigInt 只能是整数,不支持 Math 方法,JSON 序列化需要特殊处理,不能与 number 混合运算。