iX 5/2017
S. 48
Titel
Webentwicklung
Aufmacherbild

Modernes JavaScript

Handgemacht

Die JavaScript-Welt ist in permanenter Bewegung: Die Sprache entwickelt sich stürmisch weiter und zahlreiche neue Werkzeuge, Bibliotheken und Frameworks versprechen, dem Entwickler die Arbeit leichter zu machen. Dieser Artikel zeigt, wie man heutzutage mit JavaScript entwickelt.

Framework Fatigue“ ist ein Begriff, der in letzter Zeit zunehmend in der JavaScript-Welt zu hören ist. Er steht für die von vielen Programmierern als zu schnell empfundene Entwicklung der JavaScript-Frameworks und -Bibliotheken. Gefühlt schießt jede Woche ein neues Framework aus dem Boden, das verspricht, die alltäglichen Probleme eines Entwicklers noch besser zu lösen, als es alle anderen tun.

Hinzu kommen neue Werkzeuge und Techniken wie Facebooks neuer Paketmanager Yarn, das als besseres JavaScript gehandelte TypeScript und nicht zu vergessen die Weiterentwicklung von JavaScript selbst. Für Entwickler, die jeden Tag funktionierenden Code produzieren sollen, wird es immer schwieriger, den Überblick zu behalten. Welchen Stack setzt man für welche Problemstellung ein und welche Architektur wählt man, um die eigenen Anwendungen auch in Zukunft noch pflegen und weiterentwickeln zu können?

Die Sprache, die (fast) überall läuft

JavaScript hat das geschafft, was Sun mit Java immer zu erreichen versuchte: JavaScript-Code läuft auf nahezu jedem Gerät, vom PC über das Smartphone bis hin zu Haushaltsgeräten. Diese Verbreitung hat einen guten Grund: Mit JavaScript lassen sich sehr schnell Probleme lösen. Das liegt vor allem daran, dass einem die Sprache sehr viele Freiheiten lässt. Als Entwickler ist man weder an feste Datentypen noch an bestimmte Paradigmen wie klassenbasierte Objektorientierung gebunden. Genau deswegen hat JavaScript allerdings auch unter Entwicklern nicht den besten Ruf.

Bevor man JavaScript jedoch verteufelt: Auch mit C, Java oder PHP lässt sich schlechter Quellcode implementieren. Viele Designschwächen von JavaScript stammen noch aus den Ursprungszeiten der Sprache, die in den 90er-Jahren als leichtgewichtige Skriptsprache für den Netscape-Browser entwickelt wurde. Klassen, Module und die verschiedenen Design-Patterns, mit denen man heute arbeitet, waren damals noch nicht vorstellbar. Und so hat sich die Sprache über die Jahre hinweg stetig weiterentwickelt.

Nach einer längeren Durststrecke nahm die Entwicklung mit ECMAScript 2015 und 2016 in den letzten beiden Jahren an Fahrt auf. Viele neue Sprachfeatures wie das Klassenkonstrukt, das Modulsystem, Arrow Functions und zahlreiche String- und Array-Funktionen sind als Reaktion des Standardisierungskonsortiums auf die alltäglichen Probleme der JavaScript-Gemeinde entstanden. Wirft man einen Blick in den Quellcode aktueller Projekte, erkennt man, dass diese Features recht schnell angenommen werden und produktiv in Webapplikationen zum Einsatz kommen.

Viele dieser neuen Sprachfeatures verfolgen das Ziel, die Arbeit mit JavaScript einfacher zu machen. Nimmt man beispielsweise den prototypenbasierten Charakter der Sprache, stellt man schnell fest, dass das Erzeugen der Konstruktorfunktion und das Hinzufügen von Methoden wenig strukturiert ist und Entwickler geradezu dazu einlädt, die Manipulation des Prototyps nicht in einem Block durchzuführen. Ganz zu schweigen von der umständlichen Art, eine Vererbungsstruktur mit diesen Mitteln umzusetzen.

Hier ist das neue Klassenkonzept ab ECMAScript 2015 sehr hilfreich. Es erweitert die Sprache um die zusätzlichen Schlüsselwörter class und extends und ergänzt die bestehenden Sprachfeatures um entwicklerfreundliche Schnittstellen. Im Kern bleibt dabei der prototypenorientierte Ansatz der Sprache erhalten, wird jedoch durch das neue Schlüsselwort class maskiert und vereinfacht.

In der Kompatibilitätshölle: Die Unterstützung moderner JavaScript-Features variiert von Browser zu Browser (Abb. 1). Quelle: http://kangax.github.io/compat-table/es6/

Eines der größten Hindernisse beim Einsatz moderner JavaScript-Features sind die Browser. Häufig kann der Code nicht auf den neuesten Browser optimiert werden, sondern muss eine mehr oder weniger breite Spanne an verschiedenen Browsern und Endgeräten unterstützen. Ohne weitere Hilfsmittel würde das bedeuten, dass man nur die Features nutzen darf, die auch der älteste Browser unterstützt.

Neue Sprachfeatures für alte Browser

Aus diesem Grund gibt es die sogenannten Transpiler. Ihre Aufgabe ist es, die neuen JavaScript-Features in Quellcode zu übersetzen, der auch auf älteren Browsern lauffähig ist. Dabei gibt es jedoch Grenzen: Einige moderne Funktionen lassen sich nicht mit älteren JavaScript-Sprachmitteln simulieren. Das betrifft vor allem die Tail Call Optimization, die Proxy-Implementierung und einige weitere Features. Hier bleibt einem nichts anderes übrig, als zu warten, bis die Browserhersteller dies unterstützen.

Die Übersetzung des Quellcodes, also der Transpile-Vorgang, wird in der Regel einmal vor der Auslieferung der Applikation ausgeführt und nicht bei jeder Anfrage eines Clients. Das hat den Vorteil, dass die Arbeit einmal zentral auf dem Server durchgeführt wird und so die Ressourcen der Clients schont. Es gibt zwar auch On-demand-Transpiler, diese eignen sich jedoch höchstens für den Entwicklungsbetrieb.

Natürlich ist der von einem Transpiler generierte Code nicht so leistungsfähig wie die nativen Implementierungen der Features in modernen Browsern. Allerdings überwiegt der Vorteil, moderne Sprachkonstrukte verwenden so können, sodass die kleinen Performanceeinbußen in Kauf genommen werden. Mit zunehmender Browserunterstützung können die Übersetzungen für immer mehr Sprachfeatures wieder entfernt werden. Zudem generieren die Transpiler mittlerweile so guten Code, dass einem produktiven Einsatz nichts mehr im Weg steht.

Einer der bekanntesten und am weitesten verbreiteten Transpiler ist Babel. Babel weist eine modulare Struktur auf, die es erlaubt, nur die Plug-ins zu registrieren, die wirklich benötigt werden. Neben den Standard-ECMAScript-Transformationen unterstützt Babel mit seinen Plug-ins beispielsweise auch die Transformation von JSX, der Template-Syntax von React.

Bei zahlreichen Werkzeugen im JavaScript-Umfeld ist ein Trend in Richtung Plug-in-Infrastruktur zu erkennen. Diese Werkzeuge weisen einen stabilen Kern auf, der wie bei Babel lediglich eine Grundfunktion (hier: das Parsen von Quellcode) bietet. Den eigentlichen Mehrwert bringen erst die Plug-ins. Ein weiterer prominenter Vertreter dieses Ansatzes ist der Taskrunner Gulp, auf den wir später noch näher eingehen.

Typsicherheit in JavaScript