Am 28. Februar waren mein Kollege Norman Erck und ich (Oliver Ochs) für einen Abendvortrag „Enterpise JS – JS für Java Enterprise-Entwickler“ zur JUG Ostfalen in das Haus der Wissenschaft eingeladen.
Der erste Teil des Vortrags richtete sich an Enterprise-Enwickler (meist mit einem Java-EE-Hintergrund), die sich für JS interessieren, sich schnell darin einarbeiten wollen und Tipps suchen, dieses Ziel zu erreichen. Dabei wurden einige Grundlagen der Sprache vorgestellt. Dieser Teil des Vortrags sollte eher Lust auf mehr machen als fundamentales Wissen in aller Tiefe zu vermitteln.
Geschichte
JS wurde 1995/1996 von Brendan Eich als Mocha bzw. LiveScript für den Netscape Navigator entwickelt. 1996 wurde die Script-Sprache in JavaScript 1.0 im Zuge der Sun-Netscape-Alliance, einer Marketing-Kampagne, umbenannt. Auch Microsoft zog mit JScript, einem JS-Dialekt für Internet Explorer 3.0 nach. JScript wurde nicht JavaScript genannt, um keine Markenrechte von Sun zu verletzen (dies ist auch der Grund, warum heute viele Entwickler lieber von JS als von JavaScript sprechen, denn die Rechte am Namen JavaScript liegen heute bei Oracle). Schließlich wurde der Kern der Sprache als ECMAScript von der ECMA standardisiert.
Brendan Eich schwebte ein Scheme im Web-Browser vor – eine Sprache, um HTML über Skripte zu manipulieren. Seine Aufgabe war es, JS wie Java aussehen zu lassen, allerdings ähnelte das Ergebnis eher C und AWK. Zudem wurden einige Fehlentscheidungen, die sich auf den Versuch zurückführen lassen, LISP-Konstrukte in JS einzuführen, schon früh eingefroren.
Seine Designziel war u.a., eine Script-Sprache zu schaffen, die es einem Web-Designer ermöglicht, Code-Snippets zu kopieren und in den eigenen HTML-Code einzufügen. Darum toleriert JS Fehler wie fehlende Semikolons. Das ursprüngliche Event-Handling (onclick, onmouseover) war sehr einfach und orientierte sich an HyperCard von Apple. Eich wählte einige mächtige Grundelemente (First Class Functions aus AWK und Scheme und prototyp-basierte Objekte aus Self) und ließ viele andere, in anderen Sprachen übliche, Sprachfeatures weg.
Self – ein Vorbild für JS – war eine Sprache, die in den 1980er Jahren von Xerox PARC & Sun Microsystems entwickelt wurde. Ziel von Self war es, mehr Freiheiten als Smalltalk (eine damals beliebte objektorientierte Sprache) zu bieten. Self verwendet nur wenige Konstrukte wie Slots, Traits und Prototypen, aber keine (statischen) Typen und Klassen. Diese Konstrukte finden sich auch in JS wieder.
Self unterscheidet nicht zwischen dem Verhalten eines Objekts (Methoden einer Klasse) und dem Zustand des Objekts (Eigenschaften einer Klasse). Ein Objekt besteht aus Slots, die einen Namen haben und Methoden oder Attribute aufnehmen können. Objekten können zur Laufzeit Slots hinzugefügt werden. Objekterzeugung erfolgt vollständig ohne Klassen. Neue Objekte erzeugt man durch das Klonen existierender Objekte. Einzelne Slots können an einen Parent delegieren, indem sie den Slot aus dem Parent-Objekt nicht überschreiben. Trait-Objekte sind abgespeckte Implementierungen (Mixins).
Brendan Eich begründet die Existenz der beiden Programmiersprachen – Java und JS – mit dem Argument der Arbeitsteilung: Java war für hochpreisige Komponenten und Widgets vorgesehen, die in Java Applets laufen sollten. JS war für den Massenmarkt und Web-Designer gedacht, die lediglich Glue-Code zwischen Komponenten schreiben, Java Applets ansprechen oder Form-Validierungen bzw. Image-Rollovers implementieren möchten. Allerdings ist in der letzten Dekade Java aus der Client-Seite des Webs fast komplett verschwunden.
Im Jahr 2000 wurde ECMAScript 3rd Edition (ES3) spezifiziert. Dies ist der JS-Dialekt, den heute nahezu alle Browser sprechen. Erst zehn Jahre später wurde der Nachfolge-Standard ES5 fertiggestellt. Dies liegt daran, dass sich das Konsortium um ECMAScript nicht über die Features von ES4 einigen konnte, das u.a. Klassen und eine statische Typsierung bieten sollte. Ideen aus ES4 werden jedoch in ES6 einfließen, das voraussichtlich noch in diesem Jahr fertiggestellt wird.
Dynamische Typisierung
Als Java Enterprise-Entwickler ist man oft der Meinung, dass sich nur in einer statisch typisierten Sprache zuverlässiger Code schreiben lässt. JS kennt aber keine statische Typisierung, sondern Typen werden dynamisch zur Laufzeit ermittelt.
JS kennt fünf Basistypen. Diese sind Zahlen (number), Strings (string), Booleans (boolean), null und undefined. Wenn diese als Literale verwendet werden, sind sie unveränderlich (immutable).
Objekte
Obwohl es Basistypen gibt, so könnte ein Java Enterprise-Entwickler einwerfen, ist JS lediglich eine Script-Sprache. Eine strukturierte, objektorientierte Programmierung ist ohne Objekte nicht möglich.
In JS sind alle Datentypen außer Basistypen Objekte. Dies sind Arrays, Funktionen, Reguläre Ausdrücke (Funktionen) und Objekte selbst.
Prototypen
Der Java Enterprise-Entwickler könnte nun dagegen halten, dass eine objektorientierte Programmierung nur klassenbasiert möglich ist, da man ansonsten kein eigenes Typsystem entwickeln kann. JS bietet keine Klassen und damit kein eigenes Typsystem.
Schon ES3 bietet Konstruktor-Funktionen. Objekte werden über das new-Keyword und eine Konstruktor-Funktion gebildet. Letztere beginnt per Konvention mit einem Großbuchstaben (im Gegensatz. zu „normalen“ Funktionen). Ruft sie der new-Operator auf, wird ein Objekt vom Typ der Konstruktorfunktion erzeugt und kann innerhalb dieser angereichert werden.
Für Verebung gibt es Entwurfsmuster. Wem diese zu unbequem sind, kann eine Meta-Sprache wie TypeScript verwenden, die voraussichtlich ES6 ähneln wird, aber zu lesbarem ES3 kompiliert. Üblicher als die Verwendung von Vererbung ist aber das Benutzen von Mixins, die den Traits von Self ähneln.
Funktionale Programmierung und Closures
Java wird erst mit Java SE 8 Closures bekommen, die einem Konzept der funktionalen Programmierung aus den 1970er Jahren entstammen, das JS von Scheme geerbt hat.
In JS sind Funktionen sind gleichwertig zu Objekten (eine Funktion ist ein Objekt). Funktionen können als Parameter eine andere Funktion übergeben und auch als Rückgabewert ausgegeben werden. Dies illustriert dieses Script, das der Standard-Sortierfunktion von Arrays eine Sortiervorschrift als Funktion mitgibt.
Bei der Verwendung von JS ist das Scoping für Java-Entwickler sehr gewöhnungsbedürftig. Auf den aktuellen Kontext (Scope) greift man über this
zu. Dies ist der globale Namensraum bei einem Funktionsaufruf, ein Objekt-Namensraum beim Aufruf einer Konstruktor-Funktion mit dem new-Operator, ein Objekt-Namensraum bei einem Methodenaufruf, das Aufrufer-Objekt bei Events (im Browser) und (hier wird es interessant) definierbarer Namensraum durch call und apply. Durch call und apply lassen sich Methoden eines Objektes im Kontext eines anderen Objekts wiederverwenden. Scoping illustriert dieses Code-Snippet.
Eine Funktion wird stets in einem Funktionsgeltungsbereich ausgeführt, der auch dann gilt, wenn in einer Funktion eine weitere, innere Funktion definiert wird. Letztere hat dann Zugriff auf die Variablen der äußeren Funktion. Dieses Verhalten wird als Closure bezeichnet. Ausnahmen sind hier this
und der Bonusparameter arguments
, da diese Parameter von jeder Funktion selbst überschrieben werden.
[code lang=“js“]
function outherFunction() {
var x = ‚Hello‘;
function innerFunction() {
return x;
}
return innerFunction();
}
outherFunction(); // Hello
outherFunction.x; // undefined
[/code]
Im oben aufgeführten Beispiel wäre es möglich, Closures zur Implementierung privater Variablen zu nutzen.
Namespaces und Module
Ein Java Enterprise-Entwickler könnte der Ansicht sein, dass sich in JS Objekte und Konstruktoren definieren lassen – und mit Closures können sogar Konstrukte, die private Variablen ersetzen können, gebaut werden. Allerdings kennt JS keine Packages, ohne die sich Code nur schwer organisieren lässt. Allerdings kann man JS-Objekte als Namespaces benutzen. Dies illustrieren folgende Code-Snippets:
Über Packages hinaus muss Code in Libraries und Modulen gepflegt werden. In Java existieren dafür JARs, OSGI-Module und demnächst das standardisierte Java Module System. Die Unterstützung von JS dafür ist, was den ECMA-Standard bis Version 5 angeht, mangelhaft. Allerdings spezifiziert CommonJS ein Modulsystem, das u.a. durch AMD (require.js) implementiert wird. Dazu möchten wir diesen Blog-Post empfehlen.
Zusammenfassung und Ausblick
Zusammenfassend für den ersten Teil lässt sich sagen, dass JS als Script-Sprache für den Browser entwickelt wurde. Die Wurzeln von JS sind nicht Java, sondern exotische Sprachen, deren Konzepte niemand kennt. JS ist dynamisch typisiert, funktional und objektorientiert. Packages lassen sich über Objekte nachbilden, und für Module existieren etablierte Ansätze.
Enterprise-Entwicklung ist in der Regel Teamarbeit. Eine umfangreiche Codebasis, wie sie ein großes Team erstellen wird, muss wartbar bleiben. Im zweiten Teil des Vortrags wurden Techniken und Tools vorgestellt, die dabei helfen können, eine geeignete Codequalität zu erreichen und zu wahren. Dieser Teil des Vortrags wird in einem folgenden Blog-Post beschrieben.