做为前端开发,不知道你们是否被大整数困扰过?JavaScript 对大整型一直没有支持,想要操做大整型数字必须借助第三方库,除了麻烦还可能有打包过大和运行时效率的问题。对比 Java 中,早就有了能表示任意精度的BigInteger
。而对于 JavaScript,ECMAScript 中的提案 BigInt
就是一个能够表示任意精度的新的数字原始类型。html
本文主要围绕 BigInt
讲讲其现状、特性、进展和目前的使用方法。前端
JavaScript 中的 Number
是双精度浮点型,这意味着精度有限。Number.MAX_SAFE_INTEGER
就是安全范围内的最大值,为 2**53-1
。最小安全值为 Number.MIN_SAFE_INTEGER
值为 -((2**53)-1)
。超出安全值的计算都会丧失精度。以下,能够看到 max + 1
与 max + 2
的值相同,这显然是不对的。git
const max = Number.MAX_SAFE_INTEGER; // 9007199254740991
max + 1 // 9007199254740992
max + 2 // 9007199254740992
复制代码
至于为何最大安全值是 2**53-1
,与 IEEE 754 的 float 浮点数存储有关,可参考抓住数据的小尾巴 - JS浮点数陷阱及解法。github
实际应用中,例如在大整数 ID、高精度时间戳中会致使不安全的问题。Twitter IDs (snowflake)文中说到 Twitter 的 id 生成服务,当 id 持续增加时,就会超出 JS 的安全范围,所以要求同时冗余地返回字符串型的 id。另外一个例子,高精度时间戳在运算的时候也会丧失精度,例如使用 performance
对象与 BigInt
结合,能够获取精确到皮秒的时间戳(固然这个时间戳是否是真的精准是另外一个问题),代码以下:web
// 1 毫秒(ms) = 1,000 微秒(μs) = 1,000,000 纳秒(ns) = 1,000,000,000 皮秒(ps)
const scale = 1000000000
const scaleBig = 1000000000n
const big = BigInt((performance.now() * scale).toFixed(0)) + BigInt(performance.timing.navigationStart) * scaleBig
const normal = (performance.now() + performance.timing.navigationStart) * scale
console.log(big) // 1550488515092440117252n 精确到皮秒
console.log(normal) // 1.550488515092455e+21 精确到微秒
复制代码
在没有 BigInt 的时候,若是想要使用大整型,则不得不借助相似 BigInt 功能的第三方库。这有可能会影响 JavaScript 程序的效率,好比加载时间、解析时间、编译时间,以及运行时的效率。下图为 BigInt
与其余相似第三方库的性能对比。typescript
BigInt
是一个新的原始类型,能够实现任意精度计算。建立 BigInt
类型的值也很是简单,只须要在数字后面加上 n
便可。例如,789
变为 789n
。也能够使用全局方法 BigInt(value)
转化,入参 value 为数字或数字字符串。例如:浏览器
BigInt(1234567890) === 1234567890n // true
复制代码
另外一个例子就是上述的时间戳转换。安全
既然 BigInt
是一个新的原始类型,那么它就能够使用 typeof
检测出本身的类型bash
typeof 111 // "number"
typeof 111n // "bigint"
复制代码
同时 BigInt
与 Number
类型的值也是不严格相等的。babel
111 === 111n // false
111 == 111n // true
复制代码
在数字布尔间的逻辑中,BigInt
与 Number
表现一致。
if (0n) {
console.log('if');
} else {
console.log('else');
}
// → logs 'else', because `0n` is falsy.
复制代码
若是算上 BigInt
,JavaScript 中原始类型就从 6 个变为了 7 个。
BigInt
支持绝大部分经常使用运算符,+
, -
, *
, /
, %
, 和 **
。
位运算符 |, &, <<, >>, ^
表现也与 Number
类型中一致。
一元运算符 -
表示负数,可是 +
不能用于表示正数。由于在 webAssembly(asm.js) 中,+x
始终表示一个 Number
或异常状况。
另外就是不能混合使用 BigInt
与 Number
计算,例以下面的结果会抛出异常:
1 + 1n
// Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
复制代码
因为不能混合使用 BigInt
与 Number
,你也不能图省事将代码中全部的 Number
都用 BigInt
代替。须要视状况而定,若是数字有可能变得很大,那么再决定使用 BigInt
。
BigInt()
构造函数,相似 Number()
,能够将入参转化为 BigInt
类型。
BigInt(1) // 1n
BigInt(1.5) // RangeError
BigInt('1.5') // SyntaxError
复制代码
BigInt64Array 和 BigUint64Array
同时 BigInt
也能够精确表示64位有符号和无符号整型,全部有两个新的 TypedArray 即 BigInt64Array 和 BigUint64Array。
const view = new BigInt64Array(4);
// → [0n, 0n, 0n, 0n]
view.length;
// → 4
view[0];
// → 0n
view[0] = 42n;
view[0];
// → 42n
复制代码
目前 ES2019 的新特性都已经肯定,见 Twitter -New JavaScript features in ES2019,没有 BigInt,以下图:
➡️ Array#{flat,flatMap}
➡️ Object.fromEntries
➡️ String#{trimStart,trimEnd}
➡️ Symbol#description
➡️ try { } catch {} // optional binding
➡️ JSON ⊂ ECMAScript
➡️ well-formed JSON.stringify
➡️ stable Array#sort
➡️ revised Function#toString
复制代码
同时能够在 github 上 tc39 已完成的草案中看到。
BigInt
目前处于 Stage 3 阶段,问题不大的话,ES2020 中应该被收录。
目前(201902)浏览器支持状况并不理想,只有 Chrome 支持较好,其余浏览器支持很差。因为和其余 JavaScript 新特性不一样,BigInt 不能很好的被编译为 ES5。由于 BigInt 中修改了运算符的工做行为,这些行为是不能直接被 polyfill 转换的。
可是能够使用一个库 the JSBI library,来实现 BigInt。JSBI 是直接使用了 V8 和 Chrome 中 BigInt 的设计和实现方式,功能与浏览器中一致,语法稍有不一样:
import JSBI from './jsbi.mjs';
const max = JSBI.BigInt(Number.MAX_SAFE_INTEGER);
const two = JSBI.BigInt('2');
const result = JSBI.add(max, two);
console.log(result.toString());
// → '9007199254740993'
复制代码
一旦 BigInt 被全部的浏览器原生支持后,能够使用 babel 插件 babel-plugin-transform-jsbi-to-bigint移除 JSBI 转为原生的 BigInt 语法。例如上述代码会被转为:
const max = BigInt(Number.MAX_SAFE_INTEGER);
const two = 2n;
const result = max + two;
console.log(result);
// → '9007199254740993'
复制代码
TypeScript 3.2 已经加入了 BigInt
的类型校验。将 tsconfig 配置为 target: esnext
便可。用法示例以下:
let foo: bigint = BigInt(100); // the BigInt function
let bar: bigint = 100n; // a BigInt literal
// *Slaps roof of fibonacci function*
// This bad boy returns ints that can get *so* big!
function fibonacci(n: bigint) {
let result = 1n;
for (let last = 0n, i = 0n; i < n; i++) {
const current = result;
result += last;
last = current;
}
return result;
}
fibonacci(10000n)
复制代码
若是你肯定你的页面只跑在最新的 Chrome 中,那么如今就能够大胆的使用 BigInt
了,更优雅高效的处理大数据。若在其余浏览器中须要支持,能够使用 JSBI 这个库,往后甩掉它的姿式也十分优雅。
看着 JavaScript 愈来愈健壮,甚是欣喜。随着端计算能力的强大,AI 的发展,说不定很快就能用到这个 BigInt
特性了。