zurück zum Artikel

Neue Sprachfeatures für JavaScript im ECMAScript-6-Entwurf – Teil 2

Standards
Neue Sprachfeatures für JavaScript im ECMAScript-6-Entwurf - Teil 2

Nicht nur Objektorientierung soll demnächst Teil von ECMAScript werden, auch Sprachergänzungen wie Promises sind geplant und lassen sich schon heute mit Transpilern einsetzen.

Neben den im ersten Teil dieses Artikels [1] vorgestellten Features im Bereich der Objektorientierung gehört die Unterstützung für Arrow-Funktionen und Promises für Callbacks zu den bedeutensten Erleichterungen, die die neue Version des ECMAScript-Standards bringt. Der Transpiler Traceur ermöglicht schon heute, ihn zum programmieren für alle Browser zu nutzen.

Im Browser wird beim Umgang mit dem DOM in der Regel mit Callback-Funktionen gearbeitet. Node.js hat das Prinzip der asynchronen Verarbeitung auf den Server übertragen. Da bei JavaScript mit this nicht das aktuelle Objekt gemeint ist, sondern der aktuelle Scope, ist beim Umgang mit Callbacks in bisherigen JavaScript-Versionen die aktuelle Instanz in einer temporären Variable zu speichern. Dadurch lässt sich im Callback dann auf die Instanz und somit auf die Variable zugreifen:

var badObj = {
name: "Holger",
handleMessage: function(msg, callback) {
callback(msg);
},
doIt: function() {
var that = this;

this.handleMessage("Hi ", function(msg) {
console.log(msg + that.name);
});
}
};

badObj.doIt();

Eine Lösung sind die mit ECMAScript 6 eingeführten sogenannten Arrow-Funktionen mit der Symbolfolge =>. Sie behalten die lexikalische Bindung von this bei. Dadurch ist es beim Umgang mit Callback-Funktionen nicht mehr notwendig, den Instanz-Kontext von this in einer lokalen Variable zu speichern, um sie dann in der Callback-Funktion verwenden zu können:

var goodObj = {
name: "Martin",

handleMessage: function(msg, callback) {
callback(msg);
},

doIt: function() {
// lange Schreibweise
this.handleMessage("Guten Tag ", (msg) => {
console.log(msg + this.name);
});

// kurze Schreibweise
this.handleMessage("Hi ", msg => console.log(msg + this.name));
}

};

goodObj.doIt();

Promises und Template Strings

Ein weiterer Vorteil ist die verkürzte Schreibweise. Sie erlaubt, dass man die runden Klammern um die Parameter und die geschweiften Klammern um den Funktionsbereich optional entfallen lassen kann.

Im Umgang mit den Arrow-Funktionen gibt es drei Dinge zu beachten:

Es gibt ein paar gute Gründe, warum derartige Einschränkungen existieren; der wichtigste ist das lexikalische Binden des this-Kontext. Des Weiteren können die JavaScript-Laufzeitumgebungen den Umstand, dass nur noch benannte Parameter verwendet werden dürfen, dazu nutzen, die Ausführung zu optimieren. Ein weiterer Vorteil ist, dass sich diese Funktionen nicht mehr als Konstruktor verwenden lassen und man die JavaScript-Laufzeitumgebung durch die strikten Regeln verbessern kann.

Promises statt Verschachtelungen

Promises haben das Ziel, eine lange Verschachtelung von Callback-Aufrufen zu verhindern und somit den JavaScript-Quellcode lesbarer und damit wartbarer zu gestalten. Bibliotheken wie jQuery und AngularJS implementieren sie derzeit unterschiedlich. Da mit Promises/A+ [2] bereits eine Spezifikation exisitiert, war eine Aufnahme in den ECMAScript-Standard eine notwendige und sinnvolle Folge. Das untenstehende Beispiel zeigt eine Umsetzung eines typischen Programmablaufs unter Einsatz von Callbacks, wobei sich Entwicklern, die eine andere Sprache gelernt haben, der Ablauf nicht sofort erschließt.

function timeout(name, duration, callback) {
let doTimeout = function() {
console.log("doTimeout "+ name + " aufgerufen.");
callback(null);
};

setTimeout(doTimeout, duration);
}

try {
timeout("to_1", 100, (err) => {
if (err) {
console.log(err);
} else {
timeout("to_2", 200, (err) => {
if (err) {
console.log(err);
} else {
timeout("to_3", 300, (err) => {
if (err) {
console.log(err);
} else {
console.log("callbacks erledigt ;-)");
}
});
}
});
}
});
} catch (err) {
console.log("Fehler: " + err);
}

Betrachtet man hingegen den folgenden Ausschnitt, ist der Ablauf auf den ersten Blick verständlich.

function timeout(name, duration) {
return new Promise((resolve, reject) => {
let doTimeout = function() {
console.log("doTimeout "+ name + " aufgerufen.");
resolve();
};

setTimeout(doTimeout, duration);
})
}

let p1 = timeout("to_1", 100);
let p2 = timeout("to_2", 200);
let p3 = timeout("to_3", 300);

Promise.all([p1, p2, p3])
.then(() => {
console.log("promises erledigt ;-)");
})
.catch((err) => {
console.log(err);
});

Template Strings

Eine für viele lange fällige Ergänzung des Sprachumfangs von JavaScript ist die Einführung von Vorlagenzeichenketten (Template Strings). Bisher war es in JavaScript schwierig, die Übersicht zu behalten, wenn eine verkettete Zeichenfolge mit vielen festen und variablen Teilen zu erstellen war. Mit ${name} kann man in Zukunft innerhalb einer Zeichenkette Variablen ansprechen. In den geschweiften Klammern ist es zudem möglich, komplette Ausdrücke inklusive Funktionsaufrufe unterzubringen:

var moment = require('moment');

let world = 'Welt';

// Erstellen eines zusammengesetzten Text nach dem alten Muster.
let text_old = 'Altes hallo ' + world;
console.log(text_old);

// Erstellen eines zusammengesetzen Textes mit Hilfe von Template-Strings.
let text_new = `Neues hallo ${world}`;
console.log(text_new);

let x = 1;
let y = 2;
let rechenergebnis = `${x} + ${y} = ${x+y}`;
console.log(rechenergebnis);

function GetZeitpunkt() { return new Date(); }
let zeitpunktAusgabe = `Es ist jetzt:
${moment(GetZeitpunkt()).format("HH:MM")}`;
console.log(rechenergebnis);

Transpiler, weitere Neuerungen

Über Umwege zum Code

Da seit einiger Zeit die Notwendigkeit besteht, Enterprise-Anwendungen beziehungsweise komplexere Bibliotheken für JavaScript zu entwickeln, entstanden einige Sprachen, um die Entwicklung von JavaScript zu vereinfachen. Die geläufigsten unter ihnen sind CoffeeScript [3], TypeScript [4] und Dart [5]. Sie stellen Abstraktionen von JavaScript dar, und ein Transpiler übersetzt ihre Syntax in JavaScript, sodass jeder Browser und jede andere auf das Ausführen von JavaScript ausgelegte Umgebung damit arbeiten kann. Von Google gibt es alternativ eine spezielle Version von Chrome, die in der Lage ist, Dart direkt auszuführen.

Wenn man die JavaScript-Abstraktionssprachen, im speziellen TypeScript, betrachtet, findet man viele Analogien zur kommenden JavaScript-Version. Microsoft ist maßgeblich an der Standardisierung von ECMAScript 6 beteiligt und hat TypeScript bewusst an ihr angelehnt. Auch bei Dart finden sich einige Ähnlichkeiten zum kommenden ECMAScript-Standard wieder, zum Beispiel Klassen und Vererbung. Die Verwandschaft zur CoffeeScript-Syntax ist nicht so groß, wenngleich Konzepte wie Spreads in ECMAScript 6 übernommen wurden.

Unter dem Namen Traceur [6] gibt es bereits einen Transpiler (Compiler, der Quellcode von einer Programmiersprache in eine andere überführt) von ECMAScript-6- zu JavaScript-Code, der zu ECMAScript Version 3 oder 5 kompatibel ist. Die Entwickler von Traceur sind dabei bemüht, den Transpiler weiter voranzutreiben, sodass er bald den gesamten Sprachumfang unterstützt, soweit dies ohne Erweiterung der JavaScript-Laufzeitumgebung möglich ist. Alle Beispiele dieses Artikels sind – mit Ausnahme des WeakMap-Beispiels – mit Traceur übersetzt worden, was beweist, dass Traceur schon die wichtigsten ECMAScript 6-Features unterstützt.

Das folgende Codebeispiel zeigt, wie man eine ECMAScript-6-Datei mit Traceur übersetzt:

'use strict';

var traceur = require('traceur');
var path = require('path');

// Verwendung der als exprimentell gekennzeichneten Funktionen
traceur.options.experimental = true;

traceur.require.makeDefault(function(filename) {
// Ausnahmen definieren.
if (filename.indexOf("node_modules") != -1) {
return false;
}

// require aus node.js fuer alle anderen Dateien ueberschreiben
return true;
});

if (process.argv.length === 3) {
var filename = process.argv[2];

if (filename.indexOf("./") !== 0) {
filename = "./" + filename;
}

// Laden der JavaScript-Datei
require(filename);
} else {
console.log("ERROR: ");
console.log(" using " + path.basename(__filename) +
" <filename to use>.");
console.log("");
console.log(" sample: " + path.basename(__filename) +
"variables/sample_let.js");
process.exit();
}

Selbst bekannte Bibliotheksentwickler setzen ECMAScript 6 heute unter Verwendung von Traceur ein. Das bekannteste Beispiel dürfte Angular.js 2.0 [7] sein. Diese Version soll vollständig in ECMAScript 6 implementiert werden. Da sie zusätzlich modularer aufgebaut sein wird, steht das AngularJS-Modul für Dependency Injection [8] schon heute für den privaten Einsatz zur Verfügung. Damit zeigt sich, dass das neue ECMAScript bereits in der Praxis zum Einsatz kommt.

Weitere neue Funktionen

ECMAScript 6 enthält eine ganz Fülle von Neuerungen, sodass hier nicht alle im Detail abgehandelt werden können. Einige sollen jedoch an dieser Stelle noch Erwähnung finden:


Fazit

ECMAScript 6 erweitert die Programmiersprache JavaScript um zahlreiche Sprachfeatures, die man von einer modernen Universalprogrammiersprache erwarten darf. Gerade Entwickler, die aus Sprachen wie C++, C# und Java zu JavaScript wechseln, werden sich dadurch in JavaScript wohler fühlen.

Sobald die Spezifikation von ECMAScript 6 Mitte 2015 den Status einer Empfehlung erreicht hat , liegt es an den Browserherstellern, zügig eine Umsetzung zu liefern. Einige Browser implementieren bereits ausgewählte ECMAScript-6-Features (siehe z.B. Firefox [9]) – allerdings mit der Gefahr, dass sich der Standard noch ändern kann. Bis zur Verabschiedung des Standards kann der Transpiler Traceur eine Übergangslösung sein.

Aktuell haben TypeScript und Co den Vorteil, dass Entwicklungsumgebungen sie besser unterstützen als ECMAScript 6. Das wird sich allerdings bald ändern, sodass der direkte Einsatz eine echte Alternative wird. (jul [10])

Martin Möllenbeck und Dr. Holger Schwichtenberg
sind Entwicklungsleiter bei der 5minds IT-Solutions GmbH & Co. KG in Oberhausen. Sie entwickeln hochskalierbare Enterprise-Anwendungen mit .NET, JavaScript, Node.js und anderen zeitgemäßen Techniken.


URL dieses Artikels:
http://www.heise.de/-2426973

Links in diesem Artikel:
[1] https://www.heise.de/developer/artikel/Neue-Sprachfeatures-im-ECMAScript-6-Entwurf-Teil-1-2398267.html
[2] http://promises-aplus.github.io/promises-spec/
[3] http://www.coffeescript.org
[4] http://de.wikipedia.org/wiki/TypeScript
[5] http://www.dart-lang.org
[6] https://github.com/google/traceur-compiler
[7] http://blog.angularjs.org/2014/03/angular-20.html
[8] http://teropa.info/blog/2014/03/18/using-angular-2-0-dependency-injection-in-a-backbone-app.html
[9] https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla
[10] mailto:jul@heise.de