Blog
Mein Notizblog

Julian Strecker
TypeScript, JavaScript, Linux
Jun 20, 2020 - 24 Minuten Lesezeit

ES2020 - Die besten Neuerungen für JavaScript in 2020!

Es ist Juni 2020, vor kurzem wurden ECMAScript 2020 Spezifikationen veröffentlicht. Genau genommen reden wir über diese Spezifikation, welche den kompletten Umfang von ECMAScript in ECMA-262 edition 11 spezifiziert. Also kurz gesagt: ES2020 oder noch klarer: das neue JavaScript. Die Neuerungen, die seit der letzten Spezifikation (ES2019) dazugekommen sind, kann man im Github Projekt der TC39 finden.

In diesem kleinen Beitrag möchte ich ein paar der großen Änderungen aufgreifen und ihren Nutzen für uns (Entwickler) oder zumindest mich erklären. Die Reihenfolge ist total beliebig. Noch was: Ich bin ein bisschen Mathematik-gebiased. Und damit geht's los:

1. Exponential Operator

Möchte ich eine Potenz berechnen - zum Beispiel vier hoch 7 (4^7), dann muss ich das bis ES2019 so machen:

const vierHochSieben = Math.pow(4, 7);

Seit ES2020 kann ich das aber so schreiben:

const vierHochSieben = 4 ** 7;

Wenn ich eine Folge berechnen möchte, in der der Wert bei jeder Iteration quadriert wird, musste ich das in ES2019 so machen:

let wert = 3 // Startwert
const exponent = 2
for (let index = 0; index < letzteIteration; index++) {
  wert = Math.pow(wert, exponent);
  console.log(wert);
}

und neu:

let wert = 3 // Startwert
const exponent = 2
for (let index = 0; index < letzteIteration; index++) {
  wert **= exponent;
  console.log(wert);
}

Die Zahlenfolge (korrekt) ist übrigens folgende:

1. 9
2. 81
3. 6561
4. 43046721
5. 1853020188851841
6. 3433683820292512484657849089281
7. 11790184577738583171520872861412518665678211592275841109096961

Obwohl das Ganze jetzt nicht der Knaller ist für uns Entwickler, hat sich das Beispiel doch als ein gutes herausgestellt, nämlich als Überleitung zu einer richtig coolen Sachen (für mich). Die oben dargestellte Zahlenfolge ist "nur" die korrekte aber nicht die, die die beiden Programme wirklich erzeugen, denn ES2019 rechnet nicht genau genug.

2. BigInt - beliebig genaue Ganzzahlen

Die Zahlenreihe von eben kann ohne zusätzliche Libraries von ES2019 nicht über den fünften Wert hinaus korrekt berechnet werden. Die Genauigkeit von Number reicht nur bis 2^53+1. Der sechste Wert liegt da schon drüber. Da er (wie alle Werte in der Reihe) ungerade ist, ist seine Darstellung mit Number nicht mehr korrekt möchlich. Bis ES2019 kann ich mir in einer solchen Situation entweder mit der Ungenauigkeit abfinden oder eine Library benutzen, die die fehlende Funktionalität nachrüstet. In ES2020 kann ich aber folgendes machen:

let wert = 3n // Startwert als BigInt wegen dem n
const exponent = BigInt(2) // Alternative für ein BigInt
for (let index = 0; index < letzteIteration; index++) {
  wert **= exponent;
  console.log(wert);
}

Dieses Bild zeigt die Auswirkungen ganz gut. Wir schauen die siebte Zahl mal genauer an: NodeShell

Node Shell 1
In der ersten Zeile berechne ich die Zahl direkt. In der zweiten gebe ich sie aus (`11790...61n`). In der dritten Zeile caste ich die Zahl in eine "klassische" `Number` (`1.1790..e+61`). Dann caste ich in einem Schritt zu `Number` und wieder zurück zu `BigInt`. An der 17. Stelle ist die Ausgabe anders als bei der original-BigInt-Zahl. Ganz unten rechne ich den absoluten Unterschied zwischen der `BigInt`-Darstellung und der `Number`-Darstellung aus. Absolut betrachtet ist der Unterschied gigantisch! Relativ ist es nur ein kleiner Fehler. Aber hier - würde ich sagen - haben wir es mit einem echt wertvollen neuen Feature zu tun.

Kleiner Hinweis : Es gilt 0n == 0 aber nicht 0n === 0. 0n ist falsy und alle anderen BigInts sind truthy.

Wer mit typed Arrays arbeitet darf sich jetzt noch dieses Bild zu Gemüte führen: NodeShell

Node Shell 2

3. Optional Chaining - Bedingte Verkettung

Jetzt kommt was ganz großartiges. Sagen wir wir bekommen von irgendwo her etwas in JSON und dieses etwas bei dem wir prüfen wollen, ob es true ist. Sagen wir, die Condition ist die folgende:

if (page.header.title.visible) {
 showHeaderTitle();
}

Also wir wollen an diese color kommen. Jetzt geht es um den Fehler (wer kennt ihn nicht)

TypeError: Cannot read property 'title' of undefined

Dieser würde immer dann auftreten, wenn header nicht definiert wäre. Das müssten wir prüfen. Aber nicht nur das wir müssten alle Schritte auf dem Pfad abprüfen...

if (page && page.header && page.header.title && page.header.title.visible) {
 showHeaderTitle();
}

Das wird mit ES2020 nicht mehr nötig sein! hier können wir das ganz so abkürzen:

if (page?.header?.title?.visible) {
 showHeaderTitle();
}

4. Null Coalescing Operator

Solche Operatoren gibt es in vielen Sprachen (SQL, PHP, Perl, ... und sogar Bash). Also muss es das in JS auch geben. Zuerst aber: Was ist eigentlich ein NCO? Der NCO ist eine schöne Schreibweise um einen Fallbackwert in einer Wertzuweisung zu verwenden, falls der eigentliche Wert null oder undefined ist. Wenn ich eine Variable mit einem Wert x initialieren möchte, aber einen Fallbackwert einstellen möchte, falls x nicht angegeben ist, dann kann ich das so machen:

const name = getNameFromSomeWhere() || 'Anonym';

Aber was, wenn der Rückgabewert von getNameFromSomeWhere() zwar nicht undefined ist aber falsy. Bei einer Zahl wäre das zum Beispiel bei 0 oder 0n der Fall. Bei Strings wäre es bei '' der Fall. Wenn uns also von SomeWhere extra der leere String zurückgegeben wurde, ist es schon fies, das dann in 'Anonym' zu ändern. Das Problem ist ab ES2020 nicht mehr das unsere:

const name = getNameFromSomeWhere() ?? 'Anonym';

Das hier, also das ?? ist der NCO und diese Zeile evaluiert wirklich nur dann nach 'Anonym', wenn der Rückgabewert von getNameFromSomeWhere() entweder undefined oder null ist.

5. Array.prototype.includes

Bis ES2019 muss man, wenn man testen möchte ob ein Element in einem Array ist, ziemlich gut aufpassen. Ich mache ein Beispiel:

const array = [1,2,3,4,5];
if (array.indexOf(2) > -1) { ... }
if (array.indexOf(2) >= 0) { ... }

Diese Dinge klappen schon ganz gut, sind aber schlecht lesbar. Jetzt möchte ich aber schauen ob sich in meinem Array ein NaN versteckt hat. Hier failt unsere Lösung

const array = [1,NaN,3,4,5];
console.log(array.indexOf(NaN)); // -1 :(
console.log(NaN === NaN); // false     :(
console.log(NaN == NaN); // false      :(

Die Lösung sah für diesen Spezialfall dann so (oder so ähnlich) aus:

let nanGefunden = false;
for(element of array) {
  if (isNaN(element)) {
    nanGefunden = true;
    break;
  }
}

Für alle Fälle können wir ab ES2020 dann das Folgende schreiben:

const array = [1,NaN,3,4,5];
console.log(array.includes(3));   // true  :)
console.log(array.includes(6));   // false :)
console.log(array.includes(NaN)); // true :)

Das ist ein richtiger Mad-My-Day-Feature das ich schon jetzt sehr gerne im Einsatz habe.

6. Reguläre Ausdrücke - Punkt matcht auf alles dotAll

An den Mechaniken von regulären Ausdrücken wurde relativ viel gearbeitet. Ich das kleinste Feature raus. Nichts ist kleiner als ein Punkt und hier geht's um Punkte. In Regulären Ausdrücken kann man den Punkt ja als Platzhalter für ein beliebiges Zeichen (oder keines) nehmen.

/<span>.*<\/span>/.test('<span>abc</span>'); // true

Dieser reguläre Ausdruck <span>.*<\/span> lässt zwischen den öffnenden und schließenden Tag alles zu: Irgendwelche Buchstaben, Sonderzeichen, ... aber nicht Zeilenumbrüche (\n, \r, parSep, ...)

/<span>.*<\/span>/.test('<span>a\nbc</span>'); // false

Bis ES2019 habe ich mir mit (unter anderem) folgender Lösung helfen können:

/<span>[^]*<\/span>/.test('<span>a\nbc</span>'); // true

In den Brackets steht ein ^. Das bedeutet irgendein Zeichen außer die, die jetzt folgen. Es folgen aber keine Zeichen. Die Klammer geht wieder zu. Damit steht [^] für irgendein Zeichen. Ab ES2020 ist die folgende, viel besser lesbare Lösung möglich:

/<span>.*<\/span>/s.test('<span>a\nbc</span>'); // true

Die s-Flag (unmittelbar for .test) sorgt dafür, dass mit ihr der Dot . für alles steht. Daher dotAll. Für alle die jetzt Angst bekommen, dass sie ihre regulären Ausdrücke der letzten 15 Jahre überarbeiten müssen: Alles gut, denn die ursprüngliche Interpretation des Punktes bleibt für alle, die die s-Flag nicht gesetzt haben erhalten.

So, jetzt habe ich erstmal sechs Features vorgestellt, vielleicht schreibe ich bald nochmal weiter. Für's erst höre ich aber an dieser Stelle auf.

Komplette Liste

Die von mir vorstellten Features sind ganz bestimmt nicht die für jeden spannendsten und auch für mich nicht die einzigen relevanten oder die relevantesten. Man könnte noch so einen Artikel allein über die Änderungen im Bereich Regulärer Ausdrücke in ES2020 schreiben.

Eine Liste mit allen in ES2020 abgestimmten Fatures kannst du hier finden. Es lohnt sich mal durchzuschauen.

Unterstützung

Die vorgestellten Features sind noch lange nicht überall angekommen. Bei Node gibt es einige Features die noch gar nicht berührt wurden - hier eine Übersicht. Bei Typescript ist man für gewöhnlich schon viel weiter. Bei Browsern sieht es bestimmt noch sehr fragmentiert aus. Babel kann Preset für 2020 in dem schon viele Features abgedeckt sind.

ES2020 enthält großartige Änderungen für eine der wichtigsten Programmiersprachen. Ich freue mich darauf, so viel wie möglich dieser Features in Produktion zu nehmen.