Vor nicht allzu langer Zeit hatte
JavaScript einen neuen primitiven
BigInt- Datentyp für die Arbeit mit Zahlen mit beliebiger Genauigkeit. Das notwendige Minimum an Informationen wurde bereits über Motivation und Anwendungsfälle
erzählt /
übersetzt . Und ich möchte der übertriebenen lokalen "Explizität" beim
Typcasting und dem unerwarteten
TypeError etwas mehr Aufmerksamkeit
schenken . Werden wir schimpfen oder verstehen und (wieder) vergeben?
Implizit wird explizit?
In einer Sprache, an die implizite Typkonvertierung seit langem gewöhnt ist, ist sie zu einem Meme fast jeder Konferenz geworden, und nur wenige Menschen sind von solchen Komplikationen überrascht wie:
1 + {};
Wir bekommen plötzlich einen TypeError, der versucht, zwei scheinbar NUMMERN hinzuzufügen:
1 + 1n;
Und wenn die früheren Erfahrungen mit impliziten Aussagen nicht zu einem Zusammenbruch beim Erlernen der Sprache geführt haben, gibt es eine zweite Chance, das ECMA-Lehrbuch zusammenzubrechen und wegzuwerfen und in Java zu wechseln.
Darüber hinaus „trollt“ die Sprache weiterhin die Entwickler:
1n + '1';
Oh ja, vergessen Sie nicht den unären
+ Operator:
+1n;
Kurz gesagt, wir können
BigInt und
Number nicht in Operationen mischen. Daher wird nicht empfohlen, "große Ganzzahlen" zu verwenden, wenn 2 ^ 53-1 (
MAX_SAFE_INTEGER ) für unsere Zwecke ausreicht.
Schlüsselentscheidung
Ja, dies war die Hauptentscheidung dieser Innovation. Wenn Sie vergessen, dass dies JavaScript ist, ist alles so logisch: Diese impliziten Konvertierungen tragen zum Informationsverlust bei.
Wenn wir zwei Werte unterschiedlicher numerischer Typen (große Ganzzahlen und Gleitkommazahlen) hinzufügen, liegt der mathematische Wert des Ergebnisses möglicherweise außerhalb ihres Bereichs möglicher Werte. Beispielsweise kann der Wert des Ausdrucks
(2n ** 53n + 1n) + 0,5 durch keinen dieser Typen genau dargestellt werden. Dies ist keine Ganzzahl mehr, sondern eine reelle Zahl, aber ihre Genauigkeit wird durch das
float64-Format nicht mehr garantiert:
2n ** 53n + 1n;
In den meisten dynamischen Sprachen, in denen Typen für Ganzzahlen und Gleitkommazahlen dargestellt werden, werden die ersten als
1 und die zweiten als
1,0 geschrieben . Während arithmetischer Operationen bei Vorhandensein eines Dezimaltrennzeichens im Operanden können wir daher schließen, dass die Genauigkeit von float in Berechnungen akzeptabel ist. Aber JavaScript gehört nicht dazu, und
1 ist ein Float! Und dies bedeutet, dass die Berechnung von
2n ** 53n + 1 float 2 ^ 53
zurückgibt . Was wiederum die Schlüsselfunktionalität von
BigInt bricht:
2 ** 53 === 2 ** 53 + 1;
Es gibt auch keinen Grund, über die Implementierung des
"numerischen Turms" zu sprechen, da es Ihnen aus demselben Grund nicht gelingt, die vorhandene Nummer als allgemeinen numerischen Datentyp zu verwenden.
Um dieses Problem zu vermeiden, wurde die implizite
Umwandlung zwischen
Number und
BigInt in Operationen verboten. Infolgedessen kann die „große Ganzzahl“ nicht sicher in eine JavaScript- oder Web-API-Funktion umgewandelt werden, bei der die übliche Anzahl erwartet wird:
Math.max(1n, 10n);
Sie müssen explizit einen der beiden Typen mit
Number () oder
BigInt () auswählen .
Darüber hinaus gibt es für Operationen mit gemischten Typen
eine Erklärung für eine komplexe Implementierung oder einen Leistungsverlust, die bei Kompromiss-Sprachinnovationen häufig vorkommt.
Dies gilt natürlich für implizite numerische Konvertierungen mit anderen Grundelementen:
1 + true;
Die folgenden (bereits) Verkettungen funktionieren jedoch, da das erwartete Ergebnis eine Zeichenfolge ist:
1n + [0];
Eine weitere Ausnahme bilden Vergleichsoperatoren (wie
< ,
> und
== ) zwischen
Number und
BigInt . Es gibt auch keinen Genauigkeitsverlust, da das Ergebnis ein Boolescher Wert ist.
Wenn Sie sich an den vorherigen neuen
Symbol- Datentyp erinnern, scheint TypeError nicht mehr so radikal zu sein?
Symbol() + 1;
Und ja, aber nein. In der Tat ist das konzeptionelle Symbol überhaupt keine Zahl, sondern ein Ganzes - sehr viel:
- Es ist höchst unwahrscheinlich, dass das Symbol in eine solche Situation gerät. Dies ist jedoch sehr verdächtig und TypeError ist hier durchaus angemessen.
- Es ist sehr wahrscheinlich und üblich, dass sich das „große Ganze“ in Operationen als einer der Operanden herausstellt, wenn wirklich nichts falsch ist.
Der Operator unary
+ löst aufgrund eines Kompatibilitätsproblems mit
asm.js eine Ausnahme aus, bei der
Number erwartet wird. Das unäre Plus kann nicht wie
Big mit
BigInt arbeiten , da in diesem Fall der vorherige asm.js-Code mehrdeutig wird.
Alternatives Angebot
Trotz der relativen Einfachheit und „Sauberkeit“ der Implementierung von
BigInt betont
Axel Rauschmeyer den Mangel an Innovation. Es ist nämlich seine nur teilweise Abwärtskompatibilität mit der vorhandenen
Nummer und den folgenden:
Verwenden Sie Zahlen für bis zu 53-Bit-Ints. Verwenden Sie Ganzzahlen, wenn Sie mehr Bits benötigen
Als Alternative
schlug er Folgendes vor .
Lassen Sie
Number zum Supertyp für das neue
Int und
Double werden :
- typeof 123.0 === 'number' und Number.isDouble (123.0) === true
- typeof 123 === 'number' und Number.isInt (123) === true
Mit neuen Funktionen für die
Konvertierungen von
Number.asInt () und
Number.asDouble () . Und natürlich mit Überlastung des Bedieners und den notwendigen Abgüssen:
- Int × Double = Double (Besetzung)
- Double × Int = Double (mit Besetzung)
- Double × Double = Double
- Int × Int = Int (alle Operatoren außer Division)
Interessanterweise wird dieser Satz in der vereinfachten Version (zunächst) verwaltet, ohne der Sprache neue Typen hinzuzufügen. Stattdessen wird die
Definition des Nummerntyps erweitert: Zusätzlich zu allen möglichen 64-Bit-Zahlen mit doppelter Genauigkeit (IEEE 754-2008) enthält die Zahl jetzt alle Ganzzahlen. Infolgedessen sind die "ungenaue Nummer"
123.0 und die "genaue Nummer"
123 separate Nummern des Typs mit einer einzelnen
Nummer .
Es sieht sehr vertraut und vernünftig aus. Dies ist jedoch eine ernsthafte Aktualisierung der vorhandenen Nummer, die mit größerer Wahrscheinlichkeit das Web und seine Tools "kaputt macht":
- Es gibt einen Unterschied zwischen 1 und 1,0 , der vorher nicht da war. Der vorhandene Code verwendet sie austauschbar, was nach dem Upgrade zu Verwirrung führen kann (im Gegensatz zu Sprachen, in denen dieser Unterschied ursprünglich vorhanden war).
- Es gibt einen Effekt, wenn 1 === 1.0 (es soll ein Upgrade sein) und gleichzeitig Number.isDouble (1)! == Number.isDouble (1.0) : Auch hier ist es so.
- Die „Besonderheit“ der Gleichheit 2 ^ 53 und 2 ^ 53 + 1 verschwindet, wodurch der darauf beruhende Code gebrochen wird.
- Das gleiche Kompatibilitätsproblem mit asm.js und mehr.
Daher haben wir am Ende eine Kompromisslösung in Form eines neuen separaten Datentyps. Es ist nur hervorzuheben, dass auch eine andere Option in Betracht gezogen und
diskutiert wurde .
Wenn Sie auf zwei Stühlen sitzen
Tatsächlich beginnt
der Kommentar des
Ausschusses mit den Worten:
Finden Sie ein Gleichgewicht zwischen der Wahrung der Benutzerintuition und der Wahrung der Präzision
Einerseits wollte ich der Sprache endlich etwas „Exaktes“ hinzufügen. Und andererseits, um das für viele Entwickler bereits bekannte Verhalten beizubehalten.
Es ist nur so, dass dieses "genaue" nicht hinzugefügt werden kann, weil Sie es nicht brechen können: Mathematik, Ergonomie der Sprache, asm.js, die
Möglichkeit einer weiteren Erweiterung des Typensystems, Produktivität und letztendlich das Web selbst! Und Sie können nicht alles gleichzeitig brechen, was zum selben Ergebnis führt.
Und Sie können die Intuition der Sprachbenutzer nicht brechen, über die natürlich auch
heftig diskutiert wurde . Stimmt, hat es geklappt?