JavaScript BigInt

深入解析 JavaScript BigInt 类型:任意精度整数的表示、运算、与 number 的区别、以及在加密和金融计算中的应用。

为什么 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 混合运算。


延展阅读