10 Jahre JavaScript in 60+ Minuten

Ein Fachbeitrag von Hero Wanders, Senior Software

Vorwort

Die letzten 10 Jahre haben das JavaScript-Ökosystem maßgeblich verändert. Aus einer Script-Sprache, die von vielen Entwicklern als unschön empfunden wurde, ist eine hoch-moderne Sprache zur Entwicklung von vollwertigen Anwendungen geworden. Neben der Sprache hat sich das Tooling ebenso weiterentwickelt. Der Build-Prozess, von einem großen Projekt mit vielen Quelldateien hin zu einem kompakten und auslieferbaren Paket, ist flexibel und für verschiedene Zielplattformen steuerbar.

Nicht nur Webanwendungen lassen sich mit JavaScript entwickeln, sondern dank Node.js ebenfalls leichtgewichtige serverseitige Dienste. Fast die gesamte Tool-Chain ist ebenfalls aufgrund der Ausführungsumgebung Node.js mittlerweile selbst in JavaScript geschrieben. Liefert man eine solche Runtime, beispielsweise die JavaScript-Engine von Chrome einfach mit aus, so lassen sich auch richtige Desktop-Anwendungen paketieren – so ist es beispielsweise mit dem Editor Visual Studio Code geschehen. In diesem Artikel soll ein kleiner Überblick über den heutigen Stand der Sprache JavaScript sowie dem Tooling darum herum geben werden.

Die Sprache JavaScript

JavaScript ist eine dynamisch typisierte, objektorientierte Sprache. Dynamisch typisiert heißt: Zur Kompilierzeit werden keine Typen für Variablen vorgegeben und können sich zur Laufzeit auch noch ändern. Anstatt Typen mit Hilfe von Klassen (klassenorientiertes OOP) zu bilden, werden Prototypen (prototypenorientiertes OOP) definiert, die dann in einer Hierarchie verkettet werden. Jeder Wert, ob einfacher Boolean oder ein komplexes Objekt, hat letztlich Properties, die wiederum beliebige Werte annehmen können, z.B. Funktionen. Ohne weiteres Zutun können jedem Objekt zur Laufzeit beliebige weitere Properties hinzugefügt werden. Dies gilt auch für die Prototypen, sodass z.B. Arrays nachträglich um hilfreiche Mittel wie beispielsweise eine Summenfunktion angereicht werden können. Es liegt am Entwickler, wie sehr er diese Flexibilität nutzen möchte. Ausgehend von der JavaScript-Basis, die etwa 2009 mit EcmaScript 5 nach Jahren des Wild-wuchses standardisiert und 2011 mit EcmaScript 5.1 weiter konsolidiert wurde, gibt es seit 2015 jährliche Weiterentwicklungen dieses Sprachstandards. Hier halten nun nach und nach moderne Features, aus allen Ecken der Informatik Einzug in die Sprache. Es hat endlich eine Formalisierung von Klassen stattgefunden, die Entwickler zuvor auf verschiedene Weisen versucht hatten nachzubilden. Aber insbesondere auch die funktionale Programmierung hat großen Einfluss auf die Entwicklung der Sprache. Um aktuelle Laufzeitumgebungen – insbesondere Browser – nicht abzuhängen, wird ein Transpiler, meistens „babel“, eingesetzt, der den geschriebenen Code in einen funktional gleichwertigen Code in einem älteren Sprachstandard der Wahl übersetzt. Einige interessante Aspekte des EcmaScript-Standards:

  • Lambda-Ausdrücke
  • Funktionale Verarbeitung von Listen mit map, filter, reduce, every, some, forEach …
  • Destructuring – eine Form des Pattern Matchings (inklusive Rest-Operator) für Objekte und Arrays sowie invers dazu der Spread-Operator zum Zusammenfügen
  • Syntaktisches Konstrukt für Klassen
  • Iteration mit dem Iterable- und Iterator-Protokoll, verlegt die Logik wie iteriert wird hin zum iterierten Objekt, inklusive unendlicher Iterationen, quasi Lazy-Lists
  • Generatoren – das Schlüsselwort yield ermöglicht eine sehr komfortable Programmierung von Iteratoren
  • Promises – schon lange in verschiedener Form vorhanden, nun ist die Semantik zum und zur Verkettung asynchroner Vorgänge standardisiert
  • async/await – hiermit wird das Weiterverarbeiten von Ergebnissen asynchroner Vorgänge noch einfacher
  • Asynchrone Iteration – ein Sprachkonstrukt zur Iteration über Werte, die nach und nach asynchron geliefert werden
  • ES Modules – liefert die Syntax zum Exportieren und Importieren von Modulen
Tooling

Kein modernes System zur Anwendungsentwicklung kann langfristig ohne eine Paketverwaltung bestehen. In Java ist es z.B. Maven, in .NET NuGet etc., in JavaScript heißt das System npm (Node Package Manager). Mit dem Befehl npm install <paketname> können Pakete im aktuellen Projekt installiert werden, was in der package.json festgehalten wird. Herkunft von Paketen ist eine frei definierbare Registry. Der Standard hierfür ist npmjs.com, der der es sich um die aktuell größte Package-Registry der Welt handelt. Neben Installation von Paketen im Projekt, können auch Pakete, vornehmlich solche, die direkt ausführbare Scripte enthalten, im globalen Kontext des Benutzers installiert werden. Dies erlaubt es, nützliche (aber nicht zwingend erforderliche) Tools projektübergreifend verfügbar zu halten.
Mit zahlreichen weiteren Befehlen können Informationen zu Paketen abgerufen werden sowie Pakete auf Updates überprüft und aktualisiert werden. Ein Hilfsmittel namens npx erlaubt die Ausführung von im lokalen Kontext installierten, aber auch nicht installierten Scripten.

Build und Automatisierung

Wird eine Anwendung entwickelt, so ist bei hinreichender Komplexität ein Build-Schritt notwendig. Sei es, weil man einen modernen Sprachstandard verwenden, aber alte Browser unterstützen möchte – oder weil Bibliotheken nun gebündelt ausgeliefert werden sollen. Als eierlegende Wollmilchsau hat sich hier Webpack für Web-Anwendungen entwickelt, sowie Rollup für Bibliotheken. Hier wird der gesamte Build-Prozess abgedeckt, das reicht von Tree-Shaking zum Entfernen unnötigen Codes über Transpiling, bis zum Bundling in eine kompakte Datei und der Obfuscation zum Unkenntlichmachen von Code. Ein weiteres wichtiges Tool ist Gulp, welches einen generelleren Ansatz verfolgt und beliebige Ströme von Dateien durch eine Pipeline verarbeiten kann. Hiermit lassen sich also viele weitere Schritte oder auch ein funktionales Äquivalent von Webpack, realisieren. Zahlreiche Adapter ermöglichen den Zugriff auf bekannte Bibliotheken, wie eben z.B. Babel oder SCSS zum Transpilieren von JavaScript, TypeScript und SCSS. Als Beispiel könnte die Zusammenstellung einer Dokumentation dienen.

Testing

Eine vollständige Anwendungsentwicklung erfordert gründliche Tests. Durch Verwendung von Paketen wie Jest, Jasmine und Mocha (u.v.m.) lassen sich Unit-Test und Integrationstests schreiben. Das heißt hier werden extensive Bibliotheken von Assertion- und Mock-Funktionen angeboten, sowie Hilfsmittel zum Gruppieren von Tests. Mit Werkzeugen wie Karma oder Jest lassen sich diese Tests dann ausführen und die Ergebnisse übersichtlich auswerten. Komplettiert wird die Suite durch End-to-End-Tests. Hierbei wird beispielsweise mit Protractor ein Browser ferngesteuert, um das letztendliche Verhalten für den Benutzer zu überprüfen.

Ausblick

Es gibt weitere Techniken, die sich in den letzten paar Jahren etabliert haben. Ein sehr interessanter Ansatz nennt sich Isomorphic JavaScript – hierbei wird eine Anwendung initial auf dem Server (mit Node.js) ausgeführt und das Ergebnis als HTML-Datei zum Browser geschickt. Dieser dynamisiert („hydratisiert“) danach asynchron die Seite, indem die Anwendung nachgeladen wird. Ergebnis dieses Verfahrens ist ein stark beschleunigtes initiales Laden, sowie eine deutlich bessere Sichtbarkeit der Inhalte für Suchmaschinen. Progressive Web Applications (PWAs) sind Webanwendungen, die bestimmte Technologien vereinen, sodass sie online wie auch offline und auf allen halbwegs modernen Umgebungen ausgeführt werden, Deep-Linking zulassen, sich aber auch wie eine App verhalten. Mit einem schmalen standardisierten Browser-Wrapper lassen sie sich so beispielsweise auf Smartphones installieren und ausführen.