【前端面试小册】JS-第2节,BigInt详解 - 解决JavaScript大数精度问题
1. BigInt的诞生背景
1.1 JavaScript数字精度问题
JavaScript中的Number
类型基于IEEE 754双精度浮点数标准,存在以下限制:
// JavaScript安全整数范围
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991
// 超出安全范围会出现精度丢失
let max = Number.MAX_SAFE_INTEGER;
let max1 = max + 1;
let max2 = max + 2;
console.log(max1 === max2); // true (应该为false,但精度丢失了)
console.log(max1); // 9007199254740992
console.log(max2); // 9007199254740992 (相同!)
1.2 实际业务场景
// 金融计算中的精度问题
const price = 0.1;
const quantity = 0.2;
console.log(price + quantity); // 0.30000000000000004 (不是0.3!)
// 大数ID处理
const userId = 9007199254740992; // 超出安全范围
const nextUserId = userId + 1;
console.log(userId === nextUserId); // true (错误!)
2. BigInt解决方案
2.1 基本语法
// 创建BigInt的三种方式
const bigInt1 = BigInt(123); // 构造函数
const bigInt2 = 123n; // 字面量语法(推荐)
const bigInt3 = BigInt('123456789'); // 字符串转换
console.log(bigInt1); // 123n
console.log(bigInt2); // 123n
console.log(bigInt3); // 123456789n
2.2 解决精度问题
// 使用BigInt解决精度问题
let max = BigInt(Number.MAX_SAFE_INTEGER);
let max1 = max + 1n;
let max2 = max + 2n;
console.log(max1 === max2); // false (正确!)
console.log(max1); // 9007199254740992n
console.log(max2); // 9007199254740993n (不同!)
3. BigInt运算操作
3.1 基本算术运算
const a = 10n;
const b = 5n;
// 支持的运算
console.log(a + b); // 15n
console.log(a - b); // 5n
console.log(a * b); // 50n
console.log(a / b); // 2n (整数除法)
console.log(a % b); // 0n
console.log(a ** b); // 100000n (幂运算)
// 位运算(有符号)
console.log(a & b); // 0n (按位与)
console.log(a | b); // 15n (按位或)
console.log(a ^ b); // 15n (按位异或)
console.log(~a); // -11n (按位非)
console.log(a << 1n); // 20n (左移)
console.log(a >> 1n); // 5n (右移)
3.2 比较运算
// 与Number比较
console.log(1n < 2); // true
console.log(2n > 1); // true
console.log(2n == 2); // true (宽松相等)
console.log(2n === 2); // false (严格相等)
// 与BigInt比较
console.log(1n < 2n); // true
console.log(2n > 1n); // true
console.log(2n == 2n); // true
console.log(2n === 2n); // true
3.3 排序操作
// 混合排序
const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
console.log(mixed.sort()); // [-12n, 0, 0n, 10, 4n, 4, 6]
// 自定义排序函数
const sorted = mixed.sort((a, b) => {
// 转换为Number进行比较
const numA = typeof a === 'bigint' ? Number(a) : a;
const numB = typeof b === 'bigint' ? Number(b) : b;
return numA - numB;
});
console.log(sorted); // [-12n, 0, 0n, 4, 4n, 6, 10]
4. BigInt类型转换
4.1 与Number转换
// BigInt转Number(可能丢失精度)
const bigNum = 9007199254740992n;
const normalNum = Number(bigNum);
console.log(normalNum); // 9007199254740992
// 检查是否在安全范围内
const isSafe = (bigInt) => {
return bigInt <= Number.MAX_SAFE_INTEGER && bigInt >= Number.MIN_SAFE_INTEGER;
};
console.log(isSafe(123n)); // true
console.log(isSafe(9007199254740992n)); // false
4.2 与String转换
// BigInt转String
const bigInt = 123456789n;
console.log(String(bigInt)); // "123456789"
console.log(bigInt.toString()); // "123456789"
console.log(`${bigInt}`); // "123456789"
console.log('' + bigInt); // "123456789"
// String转BigInt
const str = "123456789";
const bigIntFromStr = BigInt(str);
console.log(bigIntFromStr); // 123456789n
5. BigInt特殊特性
5.1 零值特性
// BigInt没有+0和-0的概念
console.log(0n === -0n); // true
console.log(0n === +0n); // true
// Number有+0和-0
console.log(0 === -0); // true
console.log(Object.is(0, -0)); // false
5.2 无穷和NaN判断
// 不能直接使用全局方法
console.log(isFinite(10n)); // TypeError: Cannot convert a BigInt value to a number
console.log(isNaN(10n)); // TypeError: Cannot convert a BigInt value to a number
// 使用Number的静态方法
console.log(Number.isFinite(10n)); // false
console.log(Number.isNaN(10n)); // false
// 自定义判断函数
const isBigIntFinite = (value) => {
return typeof value === 'bigint';
};
5.3 隐式类型转换限制
// ❌ 不允许隐式转换
console.log(10n + 1); // TypeError: Cannot mix BigInt and other types
console.log(Math.max(2n, 4n, 6n)); // TypeError: Cannot convert a BigInt value to a number
// ✅ 显式转换
console.log(10n + BigInt(1)); // 11n
console.log(Math.max(Number(2n), Number(4n), Number(6n))); // 6
6. 实际应用场景
6.1 金融计算
// 精确的货币计算
class Money {
constructor(amount, currency = 'USD') {
this.amount = BigInt(Math.round(amount * 100)); // 转换为分
this.currency = currency;
}
add(other) {
if (this.currency !== other.currency) {
throw new Error('Currency mismatch');
}
return new Money(Number(this.amount + other.amount) / 100, this.currency);
}
toString() {
return `$${(Number(this.amount) / 100).toFixed(2)}`;
}
}
const price1 = new Money(19.99);
const price2 = new Money(0.01);
console.log(price1.add(price2).toString()); // $20.00
6.2 大数ID生成
// 雪花算法ID生成器
class SnowflakeIdGenerator {
constructor(workerId = 0, datacenterId = 0) {
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = 0n;
this.lastTimestamp = 0n;
}
nextId() {
const timestamp = BigInt(Date.now());
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards');
}
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1n) & 4095n; // 12位序列号
if (this.sequence === 0n) {
// 等待下一毫秒
while (timestamp <= this.lastTimestamp) {
// 等待
}
}
} else {
this.sequence = 0n;
}
this.lastTimestamp = timestamp;
// 组装ID: 41位时间戳 + 5位数据中心ID + 5位机器ID + 12位序列号
return ((timestamp - 1609459200000n) << 22n) |
(BigInt(this.datacenterId) << 17n) |
(BigInt(this.workerId) << 12n) |
this.sequence;
}
}
const generator = new SnowflakeIdGenerator(1, 1);
console.log(generator.nextId().toString()); // 生成唯一ID
6.3 大数运算工具
// 大数运算工具类
class BigIntCalculator {
// 阶乘计算
static factorial(n) {
if (n < 0n) throw new Error('Factorial of negative number');
if (n === 0n || n === 1n) return 1n;
let result = 1n;
for (let i = 2n; i <= n; i++) {
result *= i;
}
return result;
}
// 斐波那契数列
static fibonacci(n) {
if (n < 0n) throw new Error('Fibonacci of negative number');
if (n === 0n) return 0n;
if (n === 1n) return 1n;
let a = 0n, b = 1n;
for (let i = 2n; i <= n; i++) {
[a, b] = [b, a + b];
}
return b;
}
// 最大公约数
static gcd(a, b) {
a = BigInt(a);
b = BigInt(b);
while (b !== 0n) {
[a, b] = [b, a % b];
}
return a;
}
}
// 使用示例
console.log(BigIntCalculator.factorial(20n).toString()); // 2432902008176640000
console.log(BigIntCalculator.fibonacci(100n).toString()); // 354224848179261915075
console.log(BigIntCalculator.gcd(48n, 18n).toString()); // 6
7. 性能考虑
7.1 性能对比
// 性能测试
const testNumber = 1000000;
const testBigInt = 1000000n;
console.time('Number运算');
let result1 = 0;
for (let i = 0; i < 1000000; i++) {
result1 += i;
}
console.timeEnd('Number运算');
console.time('BigInt运算');
let result2 = 0n;
for (let i = 0n; i < 1000000n; i++) {
result2 += i;
}
console.timeEnd('BigInt运算');
7.2 内存使用
// BigInt内存使用更大
console.log('Number size:', 8); // 8字节
console.log('BigInt size:', '动态分配,通常更大');
8. 兼容性和最佳实践
8.1 浏览器兼容性
// 检查BigInt支持
const isBigIntSupported = typeof BigInt !== 'undefined';
if (isBigIntSupported) {
console.log('BigInt is supported');
} else {
console.log('BigInt is not supported, use polyfill');
}
8.2 最佳实践
// 1. 优先使用字面量语法
const bigInt = 123n; // 推荐
// const bigInt = BigInt(123); // 也可以
// 2. 避免不必要的类型转换
const sum = 10n + 20n; // 直接运算
// const sum = Number(10n) + Number(20n); // 避免
// 3. 处理边界情况
const safeBigIntOperation = (a, b) => {
try {
return a + b;
} catch (error) {
if (error.message.includes('Cannot mix BigInt')) {
return BigInt(a) + BigInt(b);
}
throw error;
}
};
9. 面试高频考点
9.1 常见面试题
// 1. 为什么需要BigInt?
// 答:解决JavaScript Number类型的精度限制问题
// 2. BigInt和Number的区别?
// 答:BigInt表示任意精度整数,Number基于IEEE 754双精度浮点数
// 3. 如何判断一个值是否为BigInt?
const isBigInt = (value) => typeof value === 'bigint';
// 4. BigInt可以参与哪些运算?
// 答:算术运算、比较运算、位运算,但不能与Number混合运算
// 5. 如何将BigInt转换为Number?
const bigIntToNumber = (bigInt) => {
if (bigInt > Number.MAX_SAFE_INTEGER || bigInt < Number.MIN_SAFE_INTEGER) {
throw new Error('BigInt value is outside the safe integer range');
}
return Number(bigInt);
};
10. 总结
- 核心价值:BigInt解决了JavaScript大数精度丢失问题
- 使用场景:金融计算、大数ID、密码学、科学计算
- 注意事项:不能与Number混合运算,需要显式转换
- 性能考虑:BigInt运算比Number慢,内存占用更大
- 兼容性:ES2020特性,需要现代浏览器支持
BigInt是JavaScript数字处理的重要补充,在需要精确大数计算的场景中不可或缺。
#前端面试#