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 мають бути або обидва, або жоден:

Оператори, що повертають булеві значення, дозволяють змішувати операнди-числа та BigInt:

Два оператори взагалі не підтримують BigInt:

Особливі випадки:

  • Додавання (+) рядка та BigInt повертає рядок.
  • Ділення (/) відкидає дробові частини в бік до нуля, оскільки BigInt не може представляти дробові кількості.
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
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

Через те, що перетворення між значеннями Number та значеннями BigInt можуть призводити до втрати точності, рекомендовано наступне:

  • Значення BigInt слід використовувати лише тоді, коли доцільно очікувати значень, більших за 253.
  • Не слід перетворювати між собою значення BigInt і значення Number.

Перевірки умов

Значення 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 у JavaScript можуть бути небезпечними для використання в криптографії, якщо не вживати застережних заходів. Як дуже узагальнений приклад – нападник може виміряти часову різницю між 101n ** 65537n і 17n ** 9999n, завдяки чому – оцінити потужність таємних значень, як то приватних ключів, на основі витрат часу. Якщо все ж необхідно використовувати BigInt, слід звернутися до ЧаПів атак по часу щодо загальних порад на тему цієї проблеми.

Використання всередині JSON

Застосування JSON.stringify() до будь-якого значення BigInt призведе до виринання TypeError, адже значення BigInt усталено не серіалізуються в JSON. Проте JSON.stringify() залишає саме для значень BigInt запасний хід: ця функція намагається викликати метод BigInt toJSON(). (Вона не робить цього для будь-яких інших примітивних значень.) Таким чином, можна реалізувати власний метод toJSON() (це один з тих небагатьох випадків, коли внесення змін до вбудованих об'єктів явно не знеохочується):

BigInt.prototype.toJSON = function () {
  return { $bigint: this.toString() };
};

Замість викидання помилки, тепер JSON.stringify() виробляє рядок:

console.log(JSON.stringify({ a: 1n }));
// {"a":{"$bigint":"1"}}

Коли не хочеться вносити зміни до BigInt.prototype, можна застосувати для серіалізації значень BigInt параметр JSON.stringify replacer:

const replacer = (key, value) =>
  typeof value === "bigint" ? { $bigint: value.toString() } : value;

const data = {
  number: 1,
  big: 18014398509481982n,
};
const stringified = JSON.stringify(data, replacer);

console.log(stringified);
// {"number":1,"big":{"$bigint":"18014398509481982"}}

Потім для обробки такого значення можна передати параметр reviver методу JSON.parse:

const reviver = (key, value) =>
  value !== null &&
  typeof value === "object" &&
  "$bigint" in value &&
  typeof value.$bigint === "string"
    ? BigInt(value.$bigint)
    : value;

const payload = '{"number":1,"big":{"$bigint":"18014398509481982"}}';
const parsed = JSON.parse(payload, reviver);

console.log(parsed);
// { number: 1, big: 18014398509481982n }

Примітка: Хоч замінювач для JSON.stringify() можна зробити узагальненим, і коректно серіалізувати значення BigInt для всіх можливих об'єктів, як це показано вище, відновника JSON.parse() слід використовувати з обережністю, оскільки серіалізація є втратною: неможливо відрізнити об'єкт, котрий, припустімо, просто має властивість з ім'ям $bigint, від справжнього BigInt.

Крім цього, приклад вище створює цілий об'єкт під час заміни та відновлення, що може мати вплив на продуктивність або використання пам'яті для більших об'єктів, що містять багато BigInt. Якщо форма корисного навантаження відома, може бути краще просто серіалізувати BigInt як рядки та відновлювати їх на основі імен властивостей.

Фактично JSON дозволяє числові літерали довільної довжини; їх просто не можна розібрати з повною точністю в JavaScript. Якщо відбувається комунікація з іншою програмою, написаною на мові, що підтримує довші цілі числа (наприклад, 64-бітові цілі), і необхідно передати BigInt як число JSON, а не рядок JSON, дивіться Серіалізацію чисел без втрат.

Зведення до 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 і є спільними для всіх примірників BigInt.

BigInt.prototype.constructor

Функція-конструктор, що створила об'єкт-примірник. Для примірників BigInt початкове значення – конструктор 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().

Приклади

Обчислення простих чисел

// Повертає 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
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
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

Дивіться також