Der grundlegende Leitfaden zum neuesten Datentyp von JavaScript: BigInt
Veröffentlicht: 2022-03-10Number
ganzzahlige Werte größer als 2 53 nicht sicher darstellen. Diese Einschränkung hat Entwickler gezwungen, ineffiziente Problemumgehungen und Bibliotheken von Drittanbietern zu verwenden. BigInt
ist ein neuer Datentyp, der das beheben soll. Der BigInt
-Datentyp soll es JavaScript-Programmierern ermöglichen, ganzzahlige Werte darzustellen, die größer sind als der Bereich, der vom Number
-Datentyp unterstützt wird. Die Fähigkeit, ganze Zahlen mit beliebiger Genauigkeit darzustellen, ist besonders wichtig, wenn mathematische Operationen mit großen ganzen Zahlen durchgeführt werden. Mit BigInt
ist der Integer-Überlauf kein Problem mehr.
Darüber hinaus können Sie sicher mit hochauflösenden Zeitstempeln, großen ganzzahligen IDs und mehr arbeiten, ohne eine Problemumgehung verwenden zu müssen. BigInt
ist derzeit ein Vorschlag der Stufe 3. Sobald es der Spezifikation hinzugefügt wurde, wird es der zweite numerische Datentyp in JavaScript, wodurch sich die Gesamtzahl der unterstützten Datentypen auf acht erhöht:
- Boolesch
- Null
- Nicht definiert
- Anzahl
- BigInt
- Schnur
- Symbol
- Objekt
In diesem Artikel werfen wir einen guten Blick auf BigInt
und sehen, wie es dabei helfen kann, die Einschränkungen des Number
-Typs in JavaScript zu überwinden.
Das Problem
Das Fehlen eines expliziten Integer-Typs in JavaScript ist für Programmierer, die aus anderen Sprachen kommen, oft verwirrend. Viele Programmiersprachen unterstützen mehrere numerische Typen wie Float, Double, Integer und Bignum, aber das ist bei JavaScript nicht der Fall. In JavaScript werden alle Zahlen im 64-Bit-Gleitkommaformat mit doppelter Genauigkeit gemäß der Definition des Standards IEEE 754-2008 dargestellt.
Unter diesem Standard werden sehr große ganze Zahlen, die nicht genau dargestellt werden können, automatisch gerundet. Genauer gesagt kann der Number
-Typ in JavaScript nur ganze Zahlen zwischen -9007199254740991 (-(2 53 -1)) und 9007199254740991 (2 53 -1) sicher darstellen. Jeder ganzzahlige Wert, der außerhalb dieses Bereichs liegt, kann an Genauigkeit verlieren.
Dies kann leicht untersucht werden, indem Sie den folgenden Code ausführen:
console.log(9999999999999999); // → 10000000000000000
Diese Ganzzahl ist größer als die größte Zahl, die JavaScript zuverlässig mit dem Number
darstellen kann. Daher ist es gerundet. Unerwartete Rundungen können die Zuverlässigkeit und Sicherheit eines Programms beeinträchtigen. Hier ist ein weiteres Beispiel:
// notice the last digits 9007199254740992 === 9007199254740993; // → true
JavaScript stellt die Number.MAX_SAFE_INTEGER
Konstante bereit, mit der Sie schnell die maximale sichere Ganzzahl in JavaScript abrufen können. Auf ähnliche Weise können Sie die minimale sichere Ganzzahl erhalten, indem Sie die Number.MIN_SAFE_INTEGER
Konstante verwenden:
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
Die Lösung
Um diese Einschränkungen zu umgehen, stellen einige JavaScript-Entwickler große Ganzzahlen mit dem String
-Typ dar. Die Twitter-API fügt Objekten beispielsweise eine Zeichenfolgenversion von IDs hinzu, wenn sie mit JSON antwortet. Darüber hinaus wurde eine Reihe von Bibliotheken wie bignumber.js entwickelt, um die Arbeit mit großen Ganzzahlen zu vereinfachen.
Mit BigInt
benötigen Anwendungen keine Problemumgehung oder Bibliothek mehr, um Ganzzahlen über Number.MAX_SAFE_INTEGER
und Number.Min_SAFE_INTEGER
hinaus sicher darzustellen. Arithmetische Operationen mit großen ganzen Zahlen können jetzt in Standard-JavaScript ausgeführt werden, ohne dass Genauigkeitsverluste riskiert werden. Der zusätzliche Vorteil der Verwendung eines nativen Datentyps gegenüber einer Bibliothek eines Drittanbieters ist eine bessere Laufzeitleistung.
Um eine BigInt
zu erstellen, hängen Sie einfach n
an das Ende einer Ganzzahl an. Vergleichen:
console.log(9007199254740995n); // → 9007199254740995n console.log(9007199254740995); // → 9007199254740996
Alternativ können Sie den Konstruktor BigInt()
:
BigInt("9007199254740995"); // → 9007199254740995n
BigInt
Literale können auch in binärer, oktaler oder hexadezimaler Notation geschrieben werden:
// 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
Denken Sie daran, dass Sie den strikten Gleichheitsoperator nicht verwenden können, um eine BigInt
mit einer regulären Zahl zu vergleichen, da sie nicht vom gleichen Typ sind:
console.log(10n === 10); // → false console.log(typeof 10n); // → bigint console.log(typeof 10); // → number
Stattdessen können Sie den Gleichheitsoperator verwenden, der eine implizite Typkonvertierung durchführt, bevor er seine Operanden vergleicht:
console.log(10n == 10); // → true
Alle arithmetischen Operatoren können auf BigInt
s verwendet werden, mit Ausnahme des unären Plusoperators ( +
):
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
Der Grund dafür, dass der unäre Plusoperator ( +
) nicht unterstützt wird, liegt darin, dass sich einige Programme möglicherweise auf die Invariante verlassen, dass +
immer eine Number
erzeugt oder eine Ausnahme auslöst. Das Ändern des Verhaltens von +
würde auch den asm.js-Code beschädigen.
Wenn sie mit BigInt
Operanden verwendet werden, wird natürlich erwartet, dass arithmetische Operatoren einen BigInt
Wert zurückgeben. Daher wird das Ergebnis des Divisionsoperators ( /
) automatisch abgeschnitten. Zum Beispiel:
25 / 10; // → 2.5 25n / 10n; // → 2n
Implizite Typkonvertierung
Da bei der impliziten Typkonvertierung Informationen verloren gehen können, sind gemischte Operationen zwischen BigInt
s und Number
s nicht zulässig. Beim Mischen von großen Ganzzahlen und Gleitkommazahlen kann der resultierende Wert möglicherweise nicht genau durch BigInt
oder Number
dargestellt werden. Betrachten Sie das folgende Beispiel:
(9007199254740992n + 1n) + 0.5
Das Ergebnis dieses Ausdrucks liegt außerhalb der Domäne von BigInt
und Number
. Eine Number
mit einem Bruchteil kann nicht genau in eine BigInt
konvertiert werden. Und ein BigInt
größer als 2 53 kann nicht genau in Number
umgewandelt werden.
Aufgrund dieser Einschränkung ist es nicht möglich, arithmetische Operationen mit einer Mischung aus BigInt
Number
auszuführen. Sie können auch kein BigInt
an Web-APIs und integrierte JavaScript-Funktionen übergeben, die ein Number
erwarten. Der Versuch, dies zu tun, führt zu einem TypeError
:
10 + 10n; // → TypeError Math.max(2n, 4n, 6n); // → TypeError
Beachten Sie, dass relationale Operatoren dieser Regel nicht folgen, wie in diesem Beispiel gezeigt:
10n > 5; // → true
Wenn Sie arithmetische Berechnungen mit BigInt
und Number
durchführen möchten, müssen Sie zunächst die Domäne bestimmen, in der die Operation durchgeführt werden soll. Konvertieren Sie dazu einfach einen der Operanden, indem Sie Number()
oder BigInt()
:
BigInt(10) + 10n; // → 20n // or 10 + Number(10n); // → 20
In einem Boolean
Kontext wird BigInt
ähnlich wie Number
behandelt. Mit anderen Worten, ein BigInt
wird als Wahrheitswert angesehen, solange er nicht 0n
:
if (5n) { // this code block will be executed } if (0n) { // but this code block won't }
Beim Sortieren eines Arrays findet keine implizite Typkonvertierung zwischen den Typen BigInt
und Number
statt:
const arr = [3n, 4, 2, 1n, 0, -1n]; arr.sort(); // → [-1n, 0, 1n, 2, 3n, 4]
Bitweise Operatoren wie |
, &
, <<
, >>
und ^
arbeiten mit BigInt
s auf ähnliche Weise wie Number
s. Negative Zahlen werden als unendlich langes Zweierkomplement interpretiert. Gemischte Operanden sind nicht erlaubt. Hier sind einige Beispiele:
90 | 115; // → 123 90n | 115n; // → 123n 90n | 115; // → TypeError
Der BigInt-Konstruktor
Wie bei anderen primitiven Typen kann ein BigInt
mit einer Konstruktorfunktion erstellt werden. Das an BigInt()
übergebene Argument wird nach Möglichkeit automatisch in ein BigInt
konvertiert:
BigInt("10"); // → 10n BigInt(10); // → 10n BigInt(true); // → 1n
Datentypen und Werte, die nicht konvertiert werden können, lösen eine Ausnahme aus:
BigInt(10.2); // → RangeError BigInt(null); // → TypeError BigInt("abc"); // → SyntaxError
Sie können arithmetische Operationen direkt auf einem BigInt
, das mit einem Konstruktor erstellt wurde:
BigInt(10) * 10n; // → 100n
Bei Verwendung als Operanden des strikten Gleichheitsoperators werden BigInt
s, die mit einem Konstruktor erstellt wurden, ähnlich wie normale behandelt:
BigInt(true) === 1n; // → true
Bibliotheksfunktionen
JavaScript bietet zwei Bibliotheksfunktionen zur Darstellung BigInt
Werten als Ganzzahlen mit oder ohne Vorzeichen:
-
BigInt.asUintN(width, BigInt)
: umschließt einBigInt
zwischen 0 und 2 Breite -1 -
BigInt.asIntN(width, BigInt)
: umschließt einBigInt
zwischen -2 width-1 und 2 width-1 -1
Diese Funktionen sind besonders nützlich, wenn Sie arithmetische 64-Bit-Operationen ausführen. So bleiben Sie im vorgesehenen Bereich.
Browser-Unterstützung und Transpiling
Zum Zeitpunkt der Erstellung dieses Artikels unterstützen Chrome +67 und Opera +54 den BigInt
-Datentyp vollständig. Leider haben Edge und Safari es noch nicht implementiert. Firefox unterstützt BigInt
standardmäßig nicht, aber es kann aktiviert werden, indem javascript.options.bigint
in about:config
auf true
gesetzt wird. Eine aktuelle Liste der unterstützten Browser finden Sie unter Can I use….
Unglücklicherweise ist das Transpilieren BigInt
ein äußerst komplizierter Prozess, der erhebliche Laufzeiteinbußen mit sich bringt. Es ist auch unmöglich, BigInt
direkt mit Polyfill zu füllen, da der Vorschlag das Verhalten mehrerer vorhandener Operatoren ändert. Im Moment ist eine bessere Alternative die Verwendung der JSBI-Bibliothek, die eine reine JavaScript-Implementierung des BigInt
-Vorschlags ist.
Diese Bibliothek stellt eine API bereit, die sich genauso verhält wie die native BigInt
. So können Sie JSBI verwenden:
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'
Ein Vorteil der Verwendung von JSBI besteht darin, dass Sie Ihren Code nicht mehr neu schreiben müssen, sobald sich die Browserunterstützung verbessert. Stattdessen können Sie Ihren JSBI-Code automatisch in nativen BigInt
-Code kompilieren, indem Sie ein babel-Plugin verwenden. Darüber hinaus ist die Leistung von JSBI auf Augenhöhe mit nativen BigInt
Implementierungen. Sie können bald mit einer breiteren Browserunterstützung für BigInt
rechnen.
Fazit
BigInt
ist ein neuer Datentyp, der verwendet werden soll, wenn ganzzahlige Werte größer als der vom Datentyp Number
unterstützte Bereich sind. Dieser Datentyp ermöglicht es uns, arithmetische Operationen mit großen Ganzzahlen sicher durchzuführen, hochauflösende Zeitstempel darzustellen, große Ganzzahl-IDs zu verwenden und vieles mehr, ohne dass eine Bibliothek verwendet werden muss.
Beachten Sie, dass Sie keine arithmetischen Operationen mit einer Mischung aus BigInt
Number
ausführen können. Sie müssen die Domäne bestimmen, in der die Operation ausgeführt werden soll, indem Sie einen der Operanden explizit konvertieren. Darüber hinaus dürfen Sie aus Kompatibilitätsgründen den unären Plusoperator ( +
) nicht auf einem BigInt
.
Was denken Sie? Finden Sie BigInt
nützlich? Lass es uns in den Kommentaren wissen!