做爲前端開發,不知道你們是否被大整數困擾過?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
特性了。