JavaScript内核与高级编程之:`JavaScript`的`BigInt`:其在处理大数运算时的`polyfill`。

各位靓仔靓女,晚上好!我是今晚的JS讲师,很高兴能和大家一起聊聊JavaScript的BigInt,以及在处理大数运算时,如何通过polyfill来让老浏览器也能用上这个新特性。准备好了吗?Let’s go!

第一部分:BigInt 是个啥?为啥需要它?

话说,在JavaScript的世界里,数字可不是你想多大就多大。它有一个安全整数范围,也就是Number.MAX_SAFE_INTEGER,这个家伙的值是9007199254740991。超过这个范围,你就得小心了,精度可能会丢失,导致一些奇奇怪怪的错误,比如:

console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992  咦?怎么没变?

看到了吧?加1还正常,加2就直接原地踏步了。这对于一些需要高精度计算的场景来说,简直就是噩梦。想象一下,你要计算银行利息,结果因为精度问题,少算了客户几块钱,那不得被投诉死?

BigInt就是为了解决这个问题而生的。它是一种新的数据类型,可以表示任意精度的整数,再也不用担心数字太大导致精度丢失了。

怎么用呢?很简单,直接在数字后面加个n就行了。

const bigNumber = 9007199254740991n;
console.log(bigNumber + 1n); // 9007199254740992n  这回没问题了吧!

或者,你也可以使用BigInt()构造函数来创建BigInt

const anotherBigNumber = BigInt("9007199254740991");
console.log(anotherBigNumber + 1n); // 9007199254740992n

注意: BigInt只能和BigInt进行运算,不能和普通的Number直接混用。如果你想和Number运算,需要先将Number转换为BigInt,或者将BigInt转换为Number(但是要注意精度丢失的风险)。

const number = 10;
const bigNumber = 20n;

// console.log(number + bigNumber); // 报错:TypeError: Cannot mix BigInt and other types

console.log(BigInt(number) + bigNumber); // 30n

// 小心!可能会丢失精度
console.log(Number(bigNumber) + number); // 30

第二部分:BigInt 的常见用法

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  注意:向下取整了
console.log(a % b); // 1n
console.log(a ** b); // 1000n

BigInt还支持位运算,比如与、或、异或、左移、右移等等。

const a = 10n; // 二进制:1010
const b = 3n;  // 二进制:0011

console.log(a & b);  // 2n  (1010 & 0011 = 0010)
console.log(a | b);  // 11n (1010 | 0011 = 1011)
console.log(a ^ b);  // 9n  (1010 ^ 0011 = 1001)
console.log(a << b); // 80n (1010 << 3 = 1010000)
console.log(a >> b); // 1n  (1010 >> 3 = 0001)

BigInt也可以进行比较运算,比如大于、小于、等于等等。

const a = 10n;
const b = 20n;

console.log(a > b);  // false
console.log(a < b);  // true
console.log(a == b); // false
console.log(a != b); // true
console.log(a >= b); // false
console.log(a <= b); // true

第三部分:BigInt 的浏览器兼容性

虽然BigInt很强大,但是并不是所有的浏览器都支持它。一些老旧的浏览器,比如IE,可能根本就不认识BigInt

这时候,我们就需要用到polyfill了。polyfill是一种代码,它可以让老浏览器也能支持新的JavaScript特性。

第四部分:BigIntpolyfill 实现

polyfill 的核心思想是,如果浏览器不支持 BigInt,我们就用 JavaScript 代码来模拟 BigInt 的行为。

一种常见的 BigInt polyfill 实现方式是使用数组来存储大数的每一位。例如,数字 12345678901234567890 可以存储为 [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

接下来,我们需要实现 BigInt 的各种运算,例如加法、减法、乘法、除法等等。这些运算都需要手动模拟,因为 JavaScript 内置的数字类型无法处理这么大的数字。

下面是一个简单的 BigInt polyfill 的示例代码,只实现了加法运算。

// 简易版 BigInt Polyfill
(function() {
  if (typeof BigInt !== 'undefined') {
    return; // 如果浏览器已经支持 BigInt,则直接返回
  }

  function FakeBigInt(value) {
    if (typeof value === 'number') {
      this.digits = String(value).split('').map(Number);
    } else if (typeof value === 'string') {
      this.digits = value.split('').map(Number);
    } else if (value instanceof FakeBigInt) {
      this.digits = value.digits.slice(); // 复制数组
    } else {
      throw new TypeError('Invalid argument for FakeBigInt');
    }
  }

  FakeBigInt.prototype.toString = function() {
    return this.digits.join('');
  };

  function add(a, b) {
    a = new FakeBigInt(a);
    b = new FakeBigInt(b);

    let result = [];
    let carry = 0;
    let i = a.digits.length - 1;
    let j = b.digits.length - 1;

    while (i >= 0 || j >= 0 || carry) {
      const digitA = i >= 0 ? a.digits[i] : 0;
      const digitB = j >= 0 ? b.digits[j] : 0;

      const sum = digitA + digitB + carry;
      const digit = sum % 10;
      carry = Math.floor(sum / 10);

      result.unshift(digit);
      i--;
      j--;
    }

    return new FakeBigInt(result.join(''));
  }

  window.FakeBigInt = FakeBigInt;
  window.addBigInt = add; // 暴露加法函数
})();

// 使用示例
let a = new FakeBigInt("12345678901234567890");
let b = new FakeBigInt("98765432109876543210");

let sum = addBigInt(a, b);
console.log(sum.toString()); // 输出 "111111111011111111100"

代码解释:

  1. 检查原生支持: 首先,我们检查浏览器是否已经支持 BigInt。如果支持,就直接返回,不需要 polyfill

  2. FakeBigInt 构造函数: 这个构造函数用于创建一个 FakeBigInt 对象。它可以接受一个数字、一个字符串或者另一个 FakeBigInt 对象作为参数。它将数字或字符串转换为一个数字数组,并存储在 this.digits 属性中。

  3. toString 方法: 这个方法用于将 FakeBigInt 对象转换为字符串。它将 this.digits 数组中的数字连接起来,并返回结果。

  4. add 函数: 这个函数用于计算两个 FakeBigInt 对象的和。它首先将两个参数转换为 FakeBigInt 对象,然后创建一个空数组 result 用于存储结果。接下来,它从两个数组的末尾开始,逐位计算和,并处理进位。最后,它将结果数组转换为一个 FakeBigInt 对象,并返回。

  5. 暴露 FakeBigIntaddBigInt 我们将 FakeBigInt 构造函数和 add 函数暴露给全局作用域,以便可以在其他地方使用它们。

这个 polyfill 非常简陋,只实现了加法运算,而且没有进行任何优化。但是,它可以帮助你理解 BigInt polyfill 的基本原理。

更完善的 polyfill 方案:

上面的代码只是一个示例,实际的 BigInt polyfill 需要实现更多的运算,并且需要进行优化,以提高性能。

目前比较流行的 BigInt polyfill 库有:

  • big-integer: 一个功能完善、性能优异的 BigInt 库。它提供了各种算术运算、位运算、比较运算等等,并且经过了大量的优化。
  • jsbi: 由 Google 开发的 BigInt polyfill。它旨在提供与原生 BigInt 尽可能接近的性能。

使用这些库非常简单,只需要将它们引入到你的项目中,然后就可以像使用原生 BigInt 一样使用它们了。

例如,使用 big-integer

<script src="big-integer.js"></script>
<script>
  let a = bigInt("12345678901234567890");
  let b = bigInt("98765432109876543210");

  let sum = a.add(b);
  console.log(sum.toString()); // 输出 "111111111011111111100"
</script>

第五部分:性能考量

虽然 polyfill 能够解决兼容性问题,但它也带来了一些性能上的损失。毕竟,用 JavaScript 模拟 BigInt 的行为,肯定没有原生 BigInt 快。

因此,在使用 BigInt polyfill 时,需要权衡兼容性和性能。如果你的应用对性能要求很高,那么最好尽量使用支持原生 BigInt 的浏览器。如果你的应用需要兼容老旧的浏览器,那么可以使用 polyfill,但要注意避免在大规模计算中使用 BigInt,以免影响性能。

第六部分:BigInt 的应用场景

BigInt 在很多场景下都有用武之地,比如:

  • 密码学: 密码学中经常需要进行大数运算,比如 RSA 算法。BigInt 可以方便地处理这些大数,而不用担心精度丢失。
  • 金融计算: 金融计算对精度要求非常高。BigInt 可以保证计算结果的准确性,避免出现财务上的损失。
  • 科学计算: 科学计算中也经常需要进行大数运算,比如计算天文数字、物理常数等等。BigInt 可以方便地处理这些大数,而不用自己实现大数运算的算法。
  • 游戏开发: 在一些游戏中,可能需要处理非常大的数值,比如玩家的经验值、金币数量等等。BigInt 可以保证这些数值的准确性,避免出现溢出问题。

第七部分:Number vs BigInt 对比表格

特性 Number BigInt
数据类型 浮点数 (双精度 64 位二进制格式) 任意精度的整数
安全整数范围 -2^53 到 2^53 ( Number.MAX_SAFE_INTEGER ) 没有限制
字面量表示 无后缀 后缀 n
运算 内置算术运算符 需注意类型转换,不能直接与 Number 运算
精度 精度可能丢失,尤其是在超出安全整数范围时 精度保证
浏览器兼容性 广泛支持 部分老旧浏览器不支持,需要 polyfill
使用场景 常规数值计算 需要高精度的大数运算

第八部分:总结

今天我们一起学习了 BigInt 的基本概念、常见用法、浏览器兼容性以及 polyfill 的实现方式。希望大家对 BigInt 有了更深入的了解。

BigInt 是一种非常有用的数据类型,可以解决 JavaScript 中大数运算的精度问题。虽然它有一些局限性,比如不能直接与 Number 混用,以及在老旧浏览器中需要 polyfill,但它仍然是 JavaScript 开发者工具箱中一个重要的工具。

记住,选择合适的工具,才能更好地解决问题。在需要高精度计算的场景下,BigInt 绝对是你的不二之选。

好了,今天的讲座就到这里。谢谢大家!如果有什么问题,欢迎随时提问。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注