JavaScript BigInt
什么是 BigInt?
在 JavaScript 中,标准的 Number
类型只能安全地表示范围在 -(2^53 - 1) 到 (2^53 - 1) 之间的整数,即大约 -9007199254740991 到 9007199254740991 之间的数值。这个限制在大多数日常应用中并不明显,但在处理非常大的整数时(如加密、时间戳的微秒表示或某些科学计算)会成为问题。
ES2020 引入了 BigInt
,这是 JavaScript 的第七种基本数据类型,专门用于表示任意精度的整数。无论多大的整数,BigInt 都能准确表示,不会有精度损失。
BigInt 的基本使用
创建 BigInt
有两种方法可以创建 BigInt:
- 在数字末尾添加
n
:
const bigNumber = 9007199254740991n;
console.log(bigNumber); // 输出: 9007199254740991n
- 使用
BigInt()
构造函数:
const anotherBigNumber = BigInt("9007199254740991");
console.log(anotherBigNumber); // 输出: 9007199254740991n
// 也可以直接传入数字
const yetAnotherBigNumber = BigInt(9007199254740991);
console.log(yetAnotherBigNumber); // 输出: 9007199254740991n
当使用 BigInt()
构造函数时,你可以传入一个数字或一个表示数字的字符串。
BigInt 的表现形式
BigInt 在打印时会在末尾显示 n
,这是为了区分它与普通数字:
console.log(10n); // 输出: 10n
console.log(typeof 10n); // 输出: "bigint"
BigInt 的操作
基本算术运算
BigInt 支持大多数标准的算术运算符:
console.log(10n + 20n); // 输出: 30n
console.log(10n - 5n); // 输出: 5n
console.log(10n * 10n); // 输出: 100n
console.log(10n / 3n); // 输出: 3n (注意: 结果会向零舍入)
console.log(10n % 3n); // 输出: 1n
console.log(10n ** 3n); // 输出: 1000n (10^3)
BigInt 的除法结果会向零舍入,而不是像浮点数那样提供小数部分。这是因为 BigInt 专门用于表示整数。
比较运算
BigInt 可以与其他 BigInt 或 Number 类型比较:
console.log(10n === 10); // 输出: false (类型不同)
console.log(10n == 10); // 输出: true (值相等)
console.log(10n < 15); // 输出: true
console.log(10n > 5); // 输出: true
console.log(10n <= 10); // 输出: true
不支持的操作
BigInt 不支持某些操作,尤其是那些可能导致非整数结果的操作:
// 这些会抛出错误:
// console.log(10n + 10); // 错误: 不能混合 BigInt 和其他类型
// console.log(Math.sqrt(4n)); // 错误: BigInt 不能用于 Math 对象方法
要混合 BigInt 和普通数字进行运算,需要先进行转换:
console.log(10n + BigInt(10)); // 正确: 20n
console.log(Number(10n) + 10); // 正确: 20
// 但要注意可能的精度损失
const hugeBigInt = 9007199254740993n;
console.log(Number(hugeBigInt)); // 可能不精确: 9007199254740992
BigInt 的实际应用场景
1. 处理非常大的整数
// 计算大数的阶乘
function factorial(n) {
if (n === 0n) return 1n;
return n * factorial(n - 1n);
}
console.log(factorial(50n));
// 输出: 30414093201713378043612608166064768844377641568960512000000000000n
2. 高精度时间戳
当需要微秒级或纳秒级精度的时间戳时:
// 获取高精度时间戳(如果浏览器支持)
const start = BigInt(process.hrtime.bigint ? process.hrtime.bigint() : Date.now() * 1000000);
// 执行一些操作...
const end = BigInt(process.hrtime.bigint ? process.hrtime.bigint() : Date.now() * 1000000);
console.log(`操作耗时: ${end - start} 纳秒`);
3. 密码学和哈希函数
在加密应用中处理大整数:
// 简化的大整数幂模运算 (a^b mod n)
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;
}
// 在加密应用中可能会用到类似的计算
const publicKey = 65537n;
const modulus = 2305843009213693951n;
const message = 12345678901234567890n;
const encryptedMessage = modPow(message, publicKey, modulus);
console.log("加密后的消息:", encryptedMessage);
BigInt 的限制和注意事项
- JSON 序列化:BigInt 不能直接通过
JSON.stringify()
序列化:
const bigNum = 123456789012345678901234567890n;
// 这会抛出错误
// console.log(JSON.stringify({ bigNumber: bigNum }));
// 需要自定义转换
console.log(JSON.stringify({ bigNumber: bigNum.toString() }));
- 强制类型转换:转换为 Number 可能导致精度损失:
const bigNum = 9007199254740993n;
console.log(Number(bigNum)); // 可能不精确: 9007199254740992
- 小数点:BigInt 不支持小数点,它专注于表示整数:
// 这会抛出错误
// const invalidBigInt = 10.5n;
- Math 对象:BigInt 不能用于 Math 对象的方法:
// 这会抛出错误
// Math.max(1n, 2n);
性能考虑
BigInt 操作可能比常规数字操作慢,特别是当处理非常大的数字时。因此,只有在确实需要处理超过 Number 范围的整数时才使用 BigInt。
// 性能测试示例
function measurePerformance(fn, name) {
const start = performance.now();
fn();
const end = performance.now();
console.log(`${name} 耗时: ${end - start} 毫秒`);
}
// 使用普通数字
measurePerformance(() => {
let result = 1;
for (let i = 1; i <= 1000000; i++) {
result = (result * i) % 1000000007;
}
}, "Number 操作");
// 使用 BigInt
measurePerformance(() => {
let result = 1n;
for (let i = 1n; i <= 1000000n; i++) {
result = (result * i) % 1000000007n;
}
}, "BigInt 操作");
浏览器兼容性
BigInt 是相对较新的特性,在一些旧浏览器中可能不被支持。在使用前,可以检查环境是否支持:
if (typeof BigInt !== 'undefined') {
console.log("环境支持 BigInt!");
const bigNum = 123456789012345678901234567890n;
console.log(bigNum);
} else {
console.log("环境不支持 BigInt!");
}
总结
BigInt 是 JavaScript 中专门用于处理超出普通 Number 类型范围的大整数的数据类型。它为处理金融计算、密码学、高精度时间戳等场景提供了可能。
虽然 BigInt 有一些限制(如不支持小数、不能与 Math 对象一起使用等),但它是处理大整数时的理想选择。记住,在不需要处理超大整数时,出于性能考虑,普通 Number 类型仍然是首选。
练习和深入学习
- 尝试实现一个计算斐波那契大数列的函数,并计算第 1000 个斐波那契数。
- 编写一个函数,将一个非常大的十进制数转换为二进制表示。
- 实现一个简单的大整数计算器,支持加、减、乘、除和求幂操作。
- 研究如何在 Web 应用中安全地存储和传输 BigInt 值。
推荐资源
通过掌握 BigInt,你将能够处理 JavaScript 中前所未有的大整数计算,为你的应用打开新的可能性。