JavaScript ist wie Parkett auf Teppich

In dem Buch1, das ich über JavaScript gelesen habe, stand explizit drin, dass man sich von den Merkwürdigkeiten fernhalten sollte und das schwache Typsystem nicht provozieren soll. Dann wäre es eigentlich gar nicht so schlimm. Davon konnte ich auch Abstand nehmen. Dass 2 * '2' dann 4 ergibt, aber 2 + '2' als Ergebnis '22' hat, ist halt so. Es gibt Konvertierungsregeln, und man sollte es halt lassen.

Nicht widerstehen konnte ich aber bei einigen Implementierungsdetails, die mich etwas fassungslos zurückgelassen haben. Zum Beispiel werden alle Zahlen, auch Integer, intern als Float-64 gespeichert. Das bedeutet, dass es einen Integer i gibt, ab dem i + 1 == i gilt, also die Präzision der Fließkommadarstellung nicht ausreicht. Man kann das mit diesem Code testen:

let i = 1
while (i + 1 != i) {
    console.log(i)
    i *= 2
}

Die Ausgabe ist die Zahl 9.007.199.254.740.992, bei der ist i + 1 == i. Das ist hoch genug für die meisten Anwendungen und hat ungefähr 52 Bit. Aber so richtig sinnvoll ist es dann auch nicht, immer diese Konvertierungen zu machen. Irgendwo muss nämlich auch gespeichert werden, dass es eigentlich ein Integer sein soll.

Dann werden Arrays als Objekte gespeichert. Und ein Objekt in JavaScript ist eine Hashmap von String auf beliebige Dinge. Für Dinge, die irgendwelche Daten darstellen sollen, passt das ziemlich gut. Allerdings ist das für numerische Arrays ziemlich bekloppt. Wenn man ein Array let a = ["Eins", "Zwei"] anlegt, dann macht JavaScript daraus intern ein Objekt {"0": "Eins", "1": "Zwei"}, also ein Objekt mit String-Schlüsseln. Greift man nun mit a[1] darauf zu, wird die 1 implizit zu "1" konvertiert und das dann im Objekt nachgeschlagen.

Auf die Idee, die Indizes einer linearen Datenstruktur in Strings umzuwandeln und daraus eine Hashmap zu machen, muss man erstmal kommen. Wahrscheinlich ist es dem Umstand geschuldet, dass JavaScript innerhalb von einem Monat spezifiziert worden ist. Und inzwischen gibt es auch spezielle Arrays für homogene Datentypen, die dann als kompaktes Array gespeichert werden. Es wurde also weiterentwickelt.

Dann war ich überrascht, dass JavaScript doch keine Semikola braucht. Man kann sie einfach weglassen. Aber die Regelung ist nicht so, wie in Python. Es ist richtig skurril: Und zwar versucht der Parser die nächste Zeile mitzulesen. Und wenn das einen Syntaxfehler geben würde, dann wird die Zeile davor für beendet erklärt. Das kann dann aber zu eignen Problemen führen. Was wäre mit diesen Zeilen hier?

if (something)
    return
i++

Der Regel nach müsste das dann als return i++ gelesen werden, obwohl das nicht die Intention war. Also gibt es noch die Sonderregel, dass hinter return die Zeile direkt aufhört, wenn da nichts mehr kommt. Möchte man also die Zeile umbrechen, dann darf man das nicht so machen:

return
    very_long_expression + on + new_line

Man darf es dann aber so schreiben:

return very_long_expression
    + on + new_line

Weil hier hinter dem return noch etwas kommt, gilt die Sonderregel nicht.

Eine weitere Sache, die mich von Python oder C++ kommend total irritiert hat, ist das Sortieren von Arrays. Wenn ich [1, 9, 10].sort() aufrufe, bekomme ich [1, 10, 9]. Das liegt daran, dass Array.sort() erstmal alle Elemente in Strings umwandelt, und diese dann lexikografisch sortiert. Das ergibt für eigentlich keine Datentypen Sinn, außer für Strings. Und die sind ja schon Strings.

Um das zu lösen, muss man eine Vergleichsfunktion mitgeben, also [1, 9, 10].sort((a, b) => a - b). Warum sich die Datentypen nicht selbst vergleichen können, erschließt sich mir bei Ganzzahlen nicht. Aber vielleicht kann man nicht systematisch den Vergleichsoperator bei Typen selbst belegen. Und bei gemischten Typen wird es auch möglicherweise schwer. Python bekommt das aber hin, unmöglich ist es also nicht.

Insgesamt fühlt sich JavaScript an, wie wenn man Parkettboden auf bestehenden Teppichboden verlegt. Es sieht ganz gut aus, aber darunter ist eine Basis, auf der man eigentlich nicht bauen möchte. Trotzdem funktioniert es erstaunlich gut, wenn man ein paar Dinge weiß und ihnen ausweicht.


  1. Horstmann, C. S. Modern JavaScript for the Impatient. (Addison-Wesley, 2020).