Основное руководство по новейшему типу данных JavaScript: BigInt
Опубликовано: 2022-03-10Number
не может безопасно представлять целочисленные значения, превышающие 2 53 . Это ограничение вынуждает разработчиков использовать неэффективные обходные пути и сторонние библиотеки. BigInt
— это новый тип данных, призванный исправить это. Тип данных BigInt
позволяет программистам JavaScript представлять целочисленные значения, превышающие диапазон, поддерживаемый типом данных Number
. Возможность представления целых чисел с произвольной точностью особенно важна при выполнении математических операций над большими целыми числами. С BigInt
целочисленное переполнение больше не будет проблемой.
Кроме того, вы можете безопасно работать с временными метками высокого разрешения, большими целочисленными идентификаторами и многим другим, не прибегая к обходным решениям. BigInt
в настоящее время находится на стадии 3. После добавления в спецификацию он станет вторым числовым типом данных в JavaScript, что доведет общее количество поддерживаемых типов данных до восьми:
- логический
- Нулевой
- Неопределенный
- Количество
- BigInt
- Нить
- Символ
- Объект
В этой статье мы внимательно рассмотрим BigInt
и посмотрим, как он может помочь преодолеть ограничения типа Number
в JavaScript.
Эта проблема
Отсутствие явного целочисленного типа в JavaScript часто сбивает с толку программистов из других языков. Многие языки программирования поддерживают несколько числовых типов, таких как float, double, integer и bignum, но это не относится к JavaScript. В JavaScript все числа представлены в 64-битном формате двойной точности с плавающей запятой, как определено стандартом IEEE 754-2008.
В соответствии с этим стандартом очень большие целые числа, которые не могут быть точно представлены, автоматически округляются. Чтобы быть точным, тип Number
в JavaScript может безопасно представлять только целые числа между -9007199254740991 (-(2 53 -1)) и 9007199254740991 (2 53 -1). Любое целочисленное значение, выпадающее из этого диапазона, может потерять точность.
Это можно легко проверить, выполнив следующий код:
console.log(9999999999999999); // → 10000000000000000
Это целое число больше, чем наибольшее число, которое JavaScript может надежно представить с помощью примитива Number
. Поэтому он округлый. Неожиданное округление может поставить под угрозу надежность и безопасность программы. Вот еще один пример:
// notice the last digits 9007199254740992 === 9007199254740993; // → true
JavaScript предоставляет константу Number.MAX_SAFE_INTEGER
, которая позволяет быстро получить максимально безопасное целое число в JavaScript. Точно так же вы можете получить минимальное безопасное целое число, используя константу Number.MIN_SAFE_INTEGER
:
const minInt = Number.MIN_SAFE_INTEGER; console.log(minInt); // → -9007199254740991 console.log(minInt - 5); // → -9007199254740996 // notice how this outputs the same value as above console.log(minInt - 4); // → -9007199254740996
Решение
Чтобы обойти эти ограничения, некоторые разработчики JavaScript представляют большие целые числа, используя тип String
. API Twitter, например, добавляет строковую версию идентификаторов к объектам при ответе в формате JSON. Кроме того, для облегчения работы с большими целыми числами был разработан ряд библиотек, таких как bignumber.js.
С BigInt
приложениям больше не нужны обходные пути или библиотеки для безопасного представления целых чисел помимо Number.MAX_SAFE_INTEGER
и Number.Min_SAFE_INTEGER
. Арифметические операции над большими целыми числами теперь можно выполнять в стандартном JavaScript без риска потери точности. Дополнительным преимуществом использования собственного типа данных по сравнению со сторонней библиотекой является более высокая производительность во время выполнения.
Чтобы создать BigInt
, просто добавьте n
в конец целого числа. Сравнивать:
console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996
В качестве альтернативы вы можете вызвать BigInt()
:
BigInt("9007199254740995"); // → 9007199254740995n
Литералы BigInt
также могут быть записаны в двоичной, восьмеричной или шестнадцатеричной системе счисления:
// binary console.log(0b100000000000000000000000000000000000000000000000000011n); // → 9007199254740995n // hex console.log(0x20000000000003n); // → 9007199254740995n // octal console.log(0o400000000000000003n); // → 9007199254740995n // note that legacy octal syntax is not supported console.log(0400000000000000003n); // → SyntaxError
Имейте в виду, что вы не можете использовать оператор строгого равенства для сравнения BigInt
с обычным числом, потому что они не одного типа:
console.log(10n === 10); // → false console.log(typeof 10n); // → bigint console.log(typeof 10); // → number
Вместо этого вы можете использовать оператор равенства, который выполняет неявное преобразование типов перед сравнением своих операндов:
console.log(10n == 10); // → true
Все арифметические операторы могут использоваться в BigInt
кроме унарного оператора плюс ( +
):
10n + 20n; // → 30n 10n - 20n; // → -10n +10n; // → TypeError: Cannot convert a BigInt value to a number -10n; // → -10n 10n * 20n; // → 200n 20n / 10n; // → 2n 23n % 10n; // → 3n 10n ** 3n; // → 1000n let x = 10n; ++x; // → 11n --x; // → 10n
Причина, по которой унарный оператор плюс ( +
) не поддерживается, заключается в том, что некоторые программы могут полагаться на инвариант, согласно которому +
всегда создает Number
или генерирует исключение. Изменение поведения +
также нарушит код asm.js.
Естественно, при использовании с операндами BigInt
, что арифметические операторы будут возвращать значение BigInt
. Поэтому результат оператора деления ( /
) автоматически усекается. Например:

25 / 10; // → 2.5 25n / 10n; // → 2n
Неявное преобразование типов
Поскольку неявное преобразование типов может привести к потере информации, смешанные операции между BigInt
и Number
не допускаются. При смешивании больших целых чисел и чисел с плавающей запятой результирующее значение может быть неточно представлено с помощью BigInt
или Number
. Рассмотрим следующий пример:
(9007199254740992n + 1n) + 0.5
Результат этого выражения находится за пределами домена как BigInt
так и Number
. Number
с дробной частью не может быть точно преобразовано в BigInt
. И BigInt
больше 2 53 не может быть точно преобразовано в Number
.
В результате этого ограничения невозможно выполнять арифметические операции с сочетанием BigInt
Number
Вы также не можете передавать BigInt
в веб-API и встроенные функции JavaScript, которые ожидают Number
. Попытка сделать это вызовет TypeError
:
10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError
Обратите внимание, что реляционные операторы не следуют этому правилу, как показано в этом примере:
10n > 5; // → true
Если вы хотите выполнять арифметические вычисления с BigInt
и Number
, вам сначала нужно определить домен, в котором должна выполняться операция. Для этого просто преобразуйте любой из операндов, вызвав Number()
или BigInt()
:
BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20
При обнаружении в Boolean
контексте BigInt
обрабатывается аналогично Number
. Другими словами, BigInt
считается истинным значением, если оно не равно 0n
:
if (5n) { // this code block will be executed } if (0n) { // but this code block won't }
При сортировке массива неявное преобразование типов между BigInt
и Number
не происходит:
const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]
Побитовые операторы, такие как |
, &
, <<
, >>
и ^
работают с BigInt
аналогично Number
. Отрицательные числа интерпретируются как дополнение до бесконечности до двух. Смешанные операнды не допускаются. Вот некоторые примеры:
90 | 115; // → 123 90n | 115n; // → 123n 90n | 115; // → TypeError
Конструктор BigInt
Как и в случае с другими примитивными типами, BigInt
можно создать с помощью функции-конструктора. Аргумент, переданный в BigInt()
, автоматически преобразуется в BigInt
, если это возможно:
BigInt("10"); // → 10n BigInt(10); // → 10n BigInt(true); // → 1n
Типы данных и значения, которые не могут быть преобразованы, вызывают исключение:
BigInt(10.2); // → RangeError BigInt(null); // → TypeError BigInt("abc"); // → SyntaxError
Вы можете напрямую выполнять арифметические операции над BigInt
, созданным с помощью конструктора:
BigInt(10) * 10n; // → 100n
При использовании в качестве операндов оператора строгого равенства BigInt
, созданные с помощью конструктора, обрабатываются аналогично обычным:
BigInt(true) === 1n; // → true
Библиотечные функции
JavaScript предоставляет две библиотечные функции для представления значений BigInt
в виде целых чисел со знаком или без знака:
-
BigInt.asUintN(width, BigInt)
: оборачиваетBigInt
между 0 и 2 шириной -1. -
BigInt.asIntN(width, BigInt)
: оборачиваетBigInt
между -2 width-1 и 2 width-1 -1
Эти функции особенно полезны при выполнении 64-битных арифметических операций. Таким образом, вы можете оставаться в пределах намеченного диапазона.
Поддержка браузера и транспиляция
На момент написания этой статьи Chrome +67 и Opera +54 полностью поддерживают тип данных BigInt
. К сожалению, Edge и Safari еще не реализовали его. Firefox не поддерживает BigInt
по умолчанию, но его можно включить, установив для параметра javascript.options.bigint
значение true
в about:config
. Актуальный список поддерживаемых браузеров доступен на странице Могу ли я использовать….
К сожалению, транспиляция BigInt
— чрезвычайно сложный процесс, который влечет за собой значительное снижение производительности во время выполнения. Также невозможно напрямую полифиллировать BigInt
потому что это предложение меняет поведение нескольких существующих операторов. На данный момент лучшей альтернативой является использование библиотеки JSBI, которая представляет собой реализацию предложения BigInt
на чистом JavaScript.
Эта библиотека предоставляет API, который ведет себя точно так же, как и собственный BigInt
. Вот как вы можете использовать JSBI:
import JSBI from './jsbi.mjs'; const b1 = JSBI.BigInt(Number.MAX_SAFE_INTEGER); const b2 = JSBI.BigInt('10'); const result = JSBI.add(b1, b2); console.log(String(result)); // → '9007199254741001'
Преимущество использования JSBI заключается в том, что после улучшения поддержки браузера вам не нужно будет переписывать свой код. Вместо этого вы можете автоматически скомпилировать код JSBI в собственный код BigInt
с помощью подключаемого модуля babel. Кроме того, производительность JSBI находится на одном уровне с собственными реализациями BigInt
. Вы можете ожидать более широкую поддержку BigInt
в браузерах в ближайшее время.
Заключение
BigInt
— это новый тип данных, предназначенный для использования, когда целые значения превышают диапазон, поддерживаемый типом данных Number
. Этот тип данных позволяет нам безопасно выполнять арифметические операции над большими целыми числами, представлять временные метки с высоким разрешением, использовать большие целые идентификаторы и многое другое без необходимости использования библиотеки.
Важно иметь в виду, что вы не можете выполнять арифметические операции со смесью BigInt
Number
Вам нужно будет определить домен, в котором должна выполняться операция, путем явного преобразования любого из операндов. Более того, по соображениям совместимости вам не разрешено использовать унарный оператор плюс ( +
) для BigInt
.
Как вы думаете? Считаете ли вы BigInt
полезным? Дайте нам знать об этом в комментариях!