BigInt
Значення BigInt
(велике ціле) представляють числові значення, котрі завеликі для представлення примітивом number
.
Опис
Значення BigInt, також іноді зване просто BigInt, є примітивом bigint
, створеним шляхом додавання в кінець літерала цілого числа n
, або ж викликом функції BigInt()
(без оператора new
) із передачею їй цілого числа або рядка.
const previouslyMaxSafeInteger = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n
const hugeString = BigInt("9007199254740991");
// 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// 9007199254740991n
const hugeOctal = BigInt("0o377777777777777777");
// 9007199254740991n
const hugeBin = BigInt(
"0b11111111111111111111111111111111111111111111111111111"
);
// 9007199254740991n
Значення BigInt у певних аспектах подібні до значень Number, але також відрізняються в кількох ключових нюансах: значення BigInt не можуть використовуватися з методами вбудованого об'єкта Math
і не можуть змішуватися в операціях зі значенням Number; усі значення повинні бути зведені до одного типу. Проте слід обережно зводити значення туди-назад, адже точність значення BigInt може бути втрачена при зведенні до значення Number.
Інформація про тип
При перевірці typeof
значення BigInt (примітив bigint
) дасть "bigint"
:
typeof 1n === "bigint"; // true
typeof BigInt("1") === "bigint"; // true
Значення BigInt також може бути загорнуто в Object
:
typeof Object(1n) === "object"; // true
Оператори
Наступні оператори можуть використовуватися зі значеннями BigInt (як примітивними, так і загорнутими в об'єкти):
+ * - % **
Побітові оператори також підтримуються, окрім >>>
(зсуву вправо з заповненням нулями), адже всі значення BigInt мають знак.
Також підтримується унарний оператор (+
), щоб не ламати asm.js.
const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER);
// 9007199254740991n
const maxPlusOne = previousMaxSafe + 1n;
// 9007199254740992n
const theFuture = previousMaxSafe + 2n;
// 9007199254740993n, це тепер працює!
const multi = previousMaxSafe * 2n;
// 18014398509481982n
const subtr = multi - 10n;
// 18014398509481972n
const mod = multi % 10n;
// 2n
const bigN = 2n ** 54n;
// 18014398509481984n
bigN * -1n;
// -18014398509481984n
Оператор /
також працює з цілими числами як очікується – проте результати операцій з дробовими результатами обрізаються при використанні зі значеннями BigInt: не буде жодної дробової частини.
const expected = 4n / 2n;
// 2n
const truncated = 5n / 2n;
// 2n, а не 2.5n
Порівняння
Значення BigInt строго не дорівнює значенню Number, проте дорівнює нестрого:
0n === 0;
// false
0n == 0;
// true
Значення Number і значення BigInt можуть порівнюватися як звично:
1n < 2;
// true
2n > 1;
// true
2 > 2;
// false
2n > 2;
// false
2n >= 2;
// true
Значення BigInt та Number можуть зустрічатися в одному масиві й сортуватися:
const mixed = [4n, 6, -12n, 10, 4, 0, 0n];
// [4n, 6, -12n, 10, 4, 0, 0n]
mixed.sort(); // усталена логіка сортування
// [ -12n, 0, 0n, 10, 4n, 4, 6 ]
mixed.sort((a, b) => a - b);
// не спрацює, адже віднімання не працює з різними типами
// TypeError: can't convert BigInt value to Number value
// сортування з адекватним числовим порівнювачем
mixed.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
// [ -12n, 0, 0n, 4n, 4, 6, 10 ]
Зверніть увагу, що порівняння загорнутих в Object
значень BigInt працює як для інших об'єктів – показує рівність лише тоді, коли порівнюється з собою один і той же примірник:
Object(0n) === 0n; // false
Object(0n) === Object(0n); // false
const o = Object(0n);
o === o; // true
Перевірки умов
Значення BigInt відповідають тим самим правилам перетворення, що й Number, коли:
- перетворюються на
Boolean
– за допомогою функціїBoolean
; - використовуються з логічними операторами
||
,&&
і!
; або - зустрічаються в перевірках умов, як то інструкціях
if
.
А саме – лише 0n
є хибністю; все решта – істинність.
if (0n) {
console.log("Привіт з if!");
} else {
console.log("Привіт з else!");
}
// "Привіт з else!"
0n || 12n;
// 12n
0n && 12n;
// 0n
Boolean(0n);
// false
Boolean(12n);
// true
!12n;
// false
!0n;
// true
Зведення до BigInt
Чимало вбудованих операцій, котрі очікують на BigInt, спершу зводять свої аргументи до BigInt. Ця операція може бути підсумована отак:
- BigInt повертаються як є.
undefined
іnull
викидаютьTypeError
.true
стає1n
;false
стає0n
.- Рядки перетворюються шляхом розбору їх так, ніби вони містять цілочисловий літерал. Будь-яка невдача розбору призводить до
SyntaxError
. Синтаксис є підмножиною рядкових літералів чисел, у якій десятковий розділювач і експоненційний запис – заборонені. - Number викидають
TypeError
для запобігання небажаному неявному зведенню, що призвело б до втрати точності. - Symbol викидають
TypeError
. - Об'єкти перетворюються на примітиви шляхом виклику їх методів
[@@toPrimitive]()
(з підказкою"number"
),valueOf()
іtoString()
– у такому порядку. Потім результівний примітив перетворюється на BigInt.
Найкращий спосіб досягнути в JavaScript майже такого ж ефекту – функція BigInt()
: BigInt(x)
використовує такий же алгоритм для перетворення x
, окрім того, що Number не викидають TypeError
, а перетворюються на BigInt, якщо є цілими числами.
Зверніть увагу, що вбудовані операції, котрі очікують на BigInt, нерідко після зведення обрізають BigInt до фіксованої ширини. Серед таких операцій – BigInt.asIntN()
, BigInt.asUintN()
, а також методи об'єктів BigInt64Array
і BigUint64Array
.
Конструктор
BigInt()
Створює нове значення BigInt
Статичні методи
BigInt.asIntN()
Обрізає значення BigInt до знакового цілого числа, і повертає його.
BigInt.asUintN()
Обрізає значення BigInt до беззнакового цілого числа, і повертає його.
Властивості примірника
BigInt.prototype[@@toStringTag]
Початкове значення
@@toStringTag
– рядок"BigInt"
. Ця властивість використовується вObject.prototype.toString()
. Проте у зв'язку з тим, щоBigInt
також має власну реалізацію методаtoString()
, ця властивість не використовується, якщо не викликатиObject.prototype.toString.call()
зі значенням BigInt якthisArg
.
Методи примірника
BigInt.prototype.toLocaleString()
Повертає рядок з чутливим до мови представленням цього значення BigInt. Заміщає метод
Object.prototype.toLocaleString()
.BigInt.prototype.toString()
Повертає рядкове представлення цього значення BigInt за заданою основою числення. Заміщає метод
Object.prototype.toString()
.BigInt.prototype.valueOf()
Повертає це значення BigInt. Заміщає метод
Object.prototype.valueOf()
.
Рекомендації щодо застосування
Зведення
Через те, що перетворення між значеннями Number та значеннями BigInt можуть призводити до втрати точності, рекомендовано наступне:
- Значення BigInt слід використовувати лише тоді, коли доцільно очікувати значень, більших за 2^53
- Не слід перетворювати між собою значення BigInt і Number.
Криптографія
Операції, котрі підтримують значення BigInt, мають не сталий час виконання, а тому вразливі до атак по часу. Таким чином, значення BigInt у JavaScript можуть бути небезпечними для використання в криптографії, якщо не вживати застережних заходів. Як дуже узагальнений приклад – нападник може виміряти часову різницю між 101n ** 65537n
і 17n ** 9999n
, завдяки чому – оцінити потужність таємних значень, як то приватних ключів, на основі витрат часу. Якщо все ж необхідно використовувати BigInt, слід звернутися до ЧаПів атак по часу щодо загальних порад на тему цієї проблеми.
Використання всередині JSON
Застосування JSON.stringify()
до будь-якого значення BigInt призведе до виринання TypeError
, адже значення BigInt усталено не серіалізуються в JSON. Проте JSON.stringify()
залишає саме для значень BigInt запасний хід: ця функція намагається викликати метод BigInt toJSON()
. (Вона не робить цього для будь-яких інших примітивних значень.) Таким чином, можна реалізувати власний метод toJSON()
(це один з тих небагатьох випадків, коли внесення змін до вбудованих об'єктів явно не знеохочується):
BigInt.prototype.toJSON = function () {
return this.toString();
};
Замість викидання помилки, тепер JSON.stringify()
виробляє рядок:
console.log(JSON.stringify({ a: 1n }));
// {"a":"1"}
Коли не хочеться вносити зміни до BigInt.prototype
, можна застосувати для серіалізації значень BigInt параметр JSON.stringify
replacer
:
const replacer = (key, value) =>
typeof value === "bigint" ? value.toString() : value;
const data = {
number: 1,
big: 18014398509481982n,
};
const stringified = JSON.stringify(data, replacer);
console.log(stringified);
// {"number":1,"big":"18014398509481982"}
Коли є дані JSON, котрі містять значення, про котрі відомо, що там будуть великі цілі числа, то для їх обробки можна використати параметр reviver
методу JSON.parse
:
const reviver = (key, value) => (key === "big" ? BigInt(value) : value);
const payload = '{"number":1,"big":"18014398509481982"}';
const parsed = JSON.parse(payload, reviver);
console.log(parsed);
// { number: 1, big: 18014398509481982n }
Примітка: Хоч замінювач для
JSON.stringify()
можна зробити узагальненим, і коректно серіалізувати значення BigInt для всіх можливих об'єктів, та відновлювач дляJSON.parse()
мусить бути конкретним для структури очікуваного корисного навантаження, тому що серіалізація відбувається зі втратами: неможливо відрізнити рядок, котрий представляє BigInt, від звичайного рядка.
Приклади
Обчислення простих чисел
// Повертає true, якщо передане значення BigInt є простим числом
function isPrime(p) {
for (let i = 2n; i * i <= p; i++) {
if (p % i === 0n) return false;
}
return true;
}
// Приймає за аргумент значення BigInt, повертає n-не просте число у вигляді значення BigInt
function nthPrime(nth) {
let maybePrime = 2n;
let prime = 0n;
while (nth >= 0n) {
if (isPrime(maybePrime)) {
nth--;
prime = maybePrime;
}
maybePrime++;
}
return prime;
}
nthPrime(20n);
// 73n
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BigInt
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |
BigInt() constructor
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |
asIntN
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |
asUintN
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |
toLocaleString
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |
toLocaleString.locales
|
Chrome Full support 76 | Edge Full support 79 | Firefox Full support 70 | Internet Explorer No support No | Opera No support No | Safari Full support 14 | WebView Android Full support 76 | Chrome Android Full support 76 | Firefox for Android Full support 79 | Opera Android Full support 54 | Safari on iOS Full support 14 | Samsung Internet Full support 12.0 | Deno Full support 1.8 | Node.js Full support 12.9.0 |
toLocaleString.options
|
Chrome Full support 76 | Edge Full support 79 | Firefox Full support 70 | Internet Explorer No support No | Opera No support No | Safari Full support 14 | WebView Android Full support 76 | Chrome Android Full support 76 | Firefox for Android Full support 79 | Opera Android Full support 54 | Safari on iOS Full support 14 | Samsung Internet Full support 12.0 | Deno Full support ?null | Node.js Full support 12.9.0 |
toString
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |
valueOf
|
Chrome Full support 67 | Edge Full support 79 | Firefox Full support 68 | Internet Explorer No support No | Opera Full support 54 | Safari Full support 14 | WebView Android Full support 67 | Chrome Android Full support 67 | Firefox for Android Full support 68 | Opera Android Full support 48 | Safari on iOS Full support 14 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.4.0 |