【前端面试小册】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数字处理的重要补充,在需要精确大数计算的场景中不可或缺。

#前端面试#
全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务