Der grundlegende Leitfaden zum neuesten Datentyp von JavaScript: BigInt

Veröffentlicht: 2022-03-10
Kurzzusammenfassung ↬ In JavaScript kann der Number 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.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

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 ein BigInt zwischen 0 und 2 Breite -1
  • BigInt.asIntN(width, BigInt) : umschließt ein BigInt 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!