Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Eine kurze Geschichte von JavaScript

JavaScript feiert den 20-jährigen Geburtstag

Eine kurze Geschichte von JavaScript

Als JavaScript im Jahr 1995 erfunden wurde, war nicht absehbar, dass die Sprache einmal die führende Programmiersprache im Web werden würde. Umfangreiche Anwendungen im Browser, auf dem Server, dem Desktop und dem Mobilgerät sind heutzutage in JavaScript geschrieben. Wie ist es dazu gekommen, welche Schritte waren nötig und wie wird es weitergehen?

Die Erfindung vor 20 Jahren

Mitte der 90er-Jahre eroberte Netscape Navigator als erster großer kommerzielle Webbrowser den Markt. Die Version 2.0 enthielt den ersten JavaScript-Interpreter, den Brendan Eich in nur 10 Tagen entwickelt hatte. JavaScript sollte eine im Vergleich zu Java-Applets einfache Möglichkeit sein, Programmcode auf einer Webseite auszuführen. Die Möglichkeiten der ersten Laufzeitumgebung waren sehr begrenzt, boten aber bereits den dynamischen Zugriff auf das HTML-Dokument, der später im Document Object Model (DOM) standardisiert wurde.

Erste Standardisierung als ECMAScript

Microsoft, der große Konkurrent von Netscape, erkannte schnell die Nützlichkeit von JavaScript und baute unter dem Namen JScript eine eigene JavaScript-Engine für seinen Internet Explorer 3.0. Die beiden Unternehmen taten sich zusammen, um den Sprachkern von JavaScript zu standardisieren. Bei der Normierungsorganisation Ecma International wurde die TC39-Arbeitsgruppe eingerichtet, die im Juni 1997 die erste Version von ECMAScript (ECMA-262) verabschiedete. Noch heute ist die TC39 für die Weiterentwicklung von JavaScript maßgeblich verantwortlich.

ECMAScript bildet den Kern von JavaScript – eine plattformübergreifende, universelle Programmiersprache. Die Browser- und HTML-spezifischen Schnittstellen hingegen wurden größtenteils vom World Wide Web Consortium (W3C) entwickelt und standardisiert. Die Trennung in Sprachkern (ECMAScript) und sogenannte Host-Umgebung (z.B. der Browser mit HTML-DOM) war schon 1995 wichtig, denn Netscape setzte JavaScript auch serverseitig ein. Später ermöglichte sie den Siegeszug von JavaScript außerhalb des Browsers.

Ajax und die Renaissance von JavaScript

Nachdem im Jahr 1999 die ECMAScript Edition 3 erschienen war, wurde es ruhig um den Sprachkern. Erst mit der Weiterentwicklung der Browser und der DOM-Schnittstelle um 2005 bekam JavaScript Aufwind. »Ajax« war in aller Munde. Die Technik XMLHttpRequest erlaubte es, im Hintergrund Inhalte per JavaScript zu laden. Erstmals entstanden dynamische Interfaces im Browser ohne Plugins und Server-Roundtrips.

Von dort an explodierte die Nutzung von JavaScript. JavaScript-Bibliotheken und Frameworks wie Prototype.js, jQuery und Dojo schossen aus dem Boden. Es wurde immer mehr JavaScript an den Browser geliefert. Mehr und mehr Menschen beschäftigten sich ernsthaft mit JavaScript.

Screenshot der Website von jQuery aus dem Jahr 2006: »New Wave Javascript - jQuery is designed to change the way you write JavaScript.«
»New Wave Javascript« – Die jQuery-Website im Jahr 2007

Derweil genoss der Shooting-Star bei den Entwicklern einen zweifelhaften Ruf. Für Neulinge bot JavaScript keine klare Struktur, sondern wartete mit bösen Überraschungen auf. Die Sprache war vergleichsweise unzugänglich und unkonventionell. Die bemerkenswerte Flexibilität bedeutete leider auch fehlende Orientierung für Lernende. Douglas Crockford bezeichnete JavaScript schon 2001 als die »am meisten missverstandenste Sprache der Welt«, um 2008 zu ergänzen, dass sie nun die populärste Sprache der Welt geworden sei.

Dabei ging es Crockford stets darum, JavaScript als eigenständige Sprache wahrzunehmen, ihr Verständnis zu verbessern und sich der Stärken bewusst zu werden. Crockford beobachtete bei Yahoo, wie JavaScript im kommerziellen Umfeld verwendet wurde und welche Probleme dabei auftraten. In seinem Buch »JavaScript: The Good Parts« von 2008 nannte er sowohl herausragende Fähigkeiten als auch Schwächen beim Namen. Ziel war es, eine JavaScript-Praxis zu etablieren, die ein robustes Entwickeln großer Anwendungen ermöglicht. Sein Engagement trug dazu bei, dass die Arbeit am Sprachkern ECMAScript wieder aufgenommen wurde und die praktischen Probleme großer JavaScript-Anwendungen angegangen wurden.

Weiterentwicklung von ECMAScript

In den Jahren 2006 und 2007 wurde zunächst an ECMAScript 4 (PDF im Web Archive) gearbeitet. Geplant war eine signifikante Erweiterung, die bekannte Konstrukte aus anderen Programmiersprachen enthalten sollte – darunter deklarative Klassen, Interfaces, Namespaces und statische Typisierung. Diese Ideen wurden zwar in den verwandten Sprachen Microsoft JScript.NET und Adobe ActionScript 3.0 erfolgreich umgesetzt, doch der radikale Umbau ging einigen in der TC39-Arbeitsgruppe zu weit. Sie befürchteten Kompatibilitätsprobleme mit bestehendem Code (vor allem im Web) und existierenden Engines. Zudem bestand die Gefahr, dass JavaScript durch die Erweiterung seinen eigentümlichen Charakter als funktionale, dynamische, prototypenbasierte Sprache verlor.

ECMAScript 5 macht die Entwicklung robust

Die Meinungsverschiedenheiten führten dazu, dass die geplante Edition 4 verworfen wurde und zunächst an einer abwärtskompatiblen Version mit behutsamen Neuerungen gearbeitet wurde. Die im Dezember 2009 veröffentlichte ECMAScript Edition 5 korrigierte zahlreiche Fehler und beseitigte Fallstricke, die das Leben schwer gemacht hatten. Außerdem wurden verschiedene nützliche Methoden hinzugefügt. Neue Array-Methoden etwa stärken den Charakter von JavaScript als objektorientierte Sprache mit funktionalen Elementen (z.B. map, reduce, filter).

Als wichtigste ECMAScript-5-Neuerung ist der Strict-Mode zu nennen, der mittels 'use strict'; angeschaltet wird. In diesem Modus verhält sich JavaScript anders: Unerwünschtes Verhalten wurde korrigiert oder deaktiviert. Beispielsweise erzeugt eine Zuweisung foo = 1 innerhalb einer Funktion keine globale Variable mehr. Das Schlüsselwort this wird nicht mehr automatisch auf das globale Objekt (im Browser window) aufgelöst. Beides waren potenzielle Fehlerquellen und sorgten für Missverständnisse. Um den Zugriff auf Objekt zu reglementieren, können Eigenschaften in ECMAScript 5 als nur lesbar markiert werden. Ganze Objekte können »versiegelt« oder sogar »eingefroren« werden.

Ziel von ECMAScript 5 war also die vorsichtige Korrektur von konzeptionellen Fehlern und Inkonsistenzen in der Sprache sowie dessen Implementierungen, die Verbesserung der Metaprogrammierung und die punktuelle Erweiterung der Kerntypen. Die kommerzielle JavaScript-Entwicklung sollte sicherer und robuster werden. Da der Strict-Mode einen Opt-In im Code erfordert, war die Abwärtskompatibilität gewährleistet.

Der große Umbau: ECMAScript 6 »Harmony«

Der seit Anfang der 2000er geplante radikale Umbau und damit die Erweiterung der Syntax wurde auf die nächste Version mit dem Codenamen »Harmony« verschoben. Im Juni 2015 wurde schließlich ECMAScript 6 verabschiedet, auch ECMAScript 2015 genannt.

Zwar gehen die neuen Sprachkonstrukte auf die über 10 Jahre alten Entwürfe zu JavaScript 2.0 / ECMAScript 4 zurück, doch es sind zwei Einflüsse zu nennen, die maßgeblich zur Entwicklung von ECMAScript 6 beigetragen haben.

Node.js bringt JavaScript auf Server und Desktop

Screenshot der Node.js-Website aus dem Jahr 2010: »Evented Input/Output for V8 JavaScript.«
»Evented I/O for V8 JavaScript« – Die Node.js-Website im Jahr 2010

Nachdem Google 2008 den Chrome-Browser mit der neuen JavaScript-Engine V8 veröffentlicht hatte, entstand 2009 die auf V8 basierende serverseitige JavaScript-Umgebung Node.js. Es entwickelte sich ein umfangreiches Software-Ökosystem, das nachhaltigen Einfluss auf den Standardisierungsprozess und auch die clientseitige JavaScript-Programmierung hatte. Zu den wegweisenden Techniken gehören die asynchrone Ein- und Ausgabe, Streams, das CommonJS-Modulsystem und der Node-Paketmanager (npm).

Metasprachen weisen den Weg

Etwa zur gleichen Zeit entstanden Metasprachen, die in JavaScript (ECMAScript 3) übersetzt wurden. Insbesondere CoffeeScript und TypeScript demonstrierten, wie die JavaScript-Syntax erweitert und vereinfacht werden konnte. Viele der neuen Sprachmerkmale von CoffeeScript, etwa Klassendeklarationen, ließen sich einfach in ECMAScript-3-Code übersetzen. Dieser »Syntaxzucker« erwies sich in der Praxis als nützlich und erhöhte die Produktivität. CoffeeScript hatte ferner zahlreiche Best Practices aus den »Good Parts« eingebaut. Namhafte Unternehmen bevorzugten CoffeeScript anstelle von »nacktem« JavaScript.

ECMAScript 6 pflastert die Trampelpfade

ECMAScript 6 ist eine Synthese dieser Ideen. Anstatt auf dem Reißbrett eine ideale Sprache zu entwerfen, wurden bestehende Praktiken aufgenommen. »Paving the cowpaths« heißt das im Jargon der Webstandards. ECMAScript 6 enthält ein deklaratives Modulsystem, das als Weiterentwicklung von CommonJS angesehen werden kann. Es übernimmt Syntaxen, die sich bei CoffeeScript bewährt hatten, z.B. deklarative Klassen als Syntaxzucker für Konstruktoren und Prototypen sowie eine Kurzschreibweise für Funktionen.

Im Zuge dessen korrigiert die ECMAScript-6-Syntax weitere Designfehler und beseitigt Stolperfallen. Mit let und const sind Variablen bzw. Konstanten möglich, deren Gültigkeit (Scope) auf einen {…}-Block anstelle auf eine Funktion begrenzt ist. Die kompakten Arrow-Functions ((…) => {…} sowie … => …) bringen eine lexikalische Auflösung des this-Schlüsselwortes mit sich – das umständliche manuelle Binding der Funktion entfällt.

ECMAScript 6 in der Praxis

Mit ECMAScript 6 ist ein großer Wurf gelungen, der die nächsten Jahre prägen wird. Schon jetzt migrieren große Projekte auf ECMAScript 6. Zwar unterstützt noch keine JavaScript-Engine vollständig den neuen Standard, der gerade einmal vor fünf Monaten erschienen ist. Es wird es noch Jahre dauern, bis ein Großteil der im Web verwendeten Browser ECMAScript 6 nativ unterstützen.

Doch es hat sich eingebürgert, den Babel-Transpiler sowie einen Module-Bundler wie browserify oder Webpack zu verwenden, um schon heute ECMAScript-6-Code zu schreiben und diesen in ECMAScript 5 zu übersetzen. Diese Praxis wurde von den besagten Metasprachen perfektioniert.

Asynchronität mit ECMAScript 6 und 7

JavaScript hat einen langen Weg zurückgelegt, ist erwachsen geworden und wurde von konzeptionellen Schwächen bereinigt. ECMAScript 6 und das npm-Ökosystem stehen für Produktivität, Robustheit, Vielseitigkeit und Spaß beim Entwickeln. Die Weiterentwicklung ruht nicht, sondern geht in großen Schritten weiter.

Eine große Stärke von JavaScript war stets die asynchrone Programmierung z.B. von Eingabe- und Ausgabeoperationen (z.B. HTTP-Anfragen, Datenbankabfragen). Üblicherweise werden Callback-Funktionen verwendet, um Erfolgs- und Fehlerfall zu behandeln. Sobald mehrere asynchrone Operationen verkettet werden, ist diese Technik schwerfällig und der Code unüberschaubar.

Von Promises zu async/await

ECMAScript 6 gliedert den etablierten Promises-Standard ein, um die Arbeit mit asynchronen Daten zu vereinfachen. Ein Promise ist ein Objekt, das einen zukünftigen Ergebniswert kapselt und in Aussicht stellt. Die Promises-API kann mit gewöhnlichem JavaScript implementiert werden. Daher lässt sich die Browserunterstützung mit Polyfills nachrüsten.

Promises erleichtern die Entwicklung enorm, doch sie benötigen immer noch gesonderte Funktionen zur Erfolgs- und Fehlerbehandlung. Wünschenswert wäre, dass sich asynchroner Code genauso einfach wie synchroner, blockierender Code schreiben lässt.

Einige versuchen, dieses Ziel mit Generator-Funktionen und dem Schlüsselwort yield aus ECMAScript 6 zu erreichen. Doch die saubere Lösung wird voraussichtlich in ECMAScript 7 kommen: Die Schlüsselwörter async und await markieren asynchrone Funktionen bzw. Funktionsaufrufe. Der Clou ist: async und await sind lediglich Syntaxzucker. Unter der Haube werden gewöhnliche Promises verwendet, jedoch ist eine nicht verschachtelte Schreibweise wie bei synchronem Code möglich.

Performance-Grenzen

Mit der Verbreitung von JavaScript entstanden immer neue Anforderungen an die Performance von JavaScript-Engines. Deren Hersteller liefern sich seit Jahren ein Kopf-an-Kopf-Rennen um die schnellste Ausführung. Doch sie stoßen zunehmend an die Grenzen der möglichen Optimierungen.

JavaScripts dynamische Natur und schwache Typisierung erschweren es, den Code der Hochsprache in effizienten Maschinencode zu übersetzen. Die Laufzeit-Performance ist schwer vorhersehbar, da heutige Just-in-time-Compiler den Code erst beim Ausführen optimieren. Die automatische Speicherbereinigung (Garbage Collection) unterbricht die Ausführung immer wieder.

Diese Performance-Grenzen zeigen sich insbesondere bei mathematischen Operationen, die für 3D-Spiele, Physik-Simulationen sowie Audio- und Videoverarbeitung nötig sind.

Reduzierung und Spezialisierung: asm.js und WebAssembly

Um mit der Performance von statisch typisierten Sprachen sowie Ahead-of-Time-Compilern mitzuhalten, entwickelte Mozilla schon 2013 die Sprache asm.js. Dies ist eine Untermenge von JavaScript, die effektiv statische Typisierung einführt und vorhersehbare Speicherverwaltung ermöglicht. Asm.js wird üblicherweise nicht von Hand geschrieben, sondern aus einer Hochsprache wie C oder C++ übersetzt.

Mitte 2015 gaben die vier großen Browserhersteller bekannt, dass sie gemeinsam an einer Weiterentwicklung von asm.js arbeiten: WebAssembly ist ein neues komprimiertes Binärformat, das maschinennahen Programmcode in Form eines abstrakten Syntaxbaumes beinhaltet. Die Binärrepräsentation erspart Browsern den Parsing-Schritt. Um die Lesbarkeit für Menschen zu gewährleisten, ist eine gesonderte Textrepräsentation geplant. Die Befehle dieser Sprache sind äußerst beschränkt, die Typen primitiv.

Technisch gesehen hat WebAssembly wenig mit JavaScript zu tun, sondern soll JavaScript dort ergänzen, wo es an seine Grenzen stößt. Wie sich JavaScript und WebAssembly zueinander verhalten und wie sie ineinander greifen, steht noch in den Sternen. Fest steht, dass Webbrowser ihre Rolle als universelle und leistungsfähige Software-Plattform ausbauen und JavaScript die zentrale Rolle spielen wird.

Links

Die Kommentare sind geschlossen.