ECMAScript 6: Die nächste Ausbaustufe von JavaScript

Veröffentlicht am 6. März 2012

ECMAScript 5 darf man spätestens seit dem Erscheinen des Internet Explorer 9 getrost als die aktuelle, tatsächlich benutzbare Version von JavaScript bezeichnen. Alle neueren Versionen der verbreiteten Browser sowie auch Node.js unterstützen den neuen Standard so weit, dass sich Entwickler nur noch von ganz alten Browser-Fossilien aufgehalten fühlen dürfen. Damit wird es also höchste Eisenbahn, sich mit der nächsten Java-Script-Version, ECMAScript 6 (auch Harmony genannt) zu befassen. Dieser Artikel ist der Auftakt zu einer Serie, die der Reihe nach der einzelnen ES6-Features unter die Lupe nimmt, sobald sie in mindestens einem Browser vernünftig implementiert sind. Dieser Beitrag betrachtet zunächst nur das Drumherum; richtig technisch wird es erst im nächsten Teil.

Geschichtlicher Überblick

Wie in der Artikelserie zu ECMAScript 5 beschrieben, handelt es sich bei ES5 um ein ein eher vorsichtiges Update, bei dem die Wahrung der Abwärtskompatibilität über allem steht. Dies ging naturgemäß so manchen Entwickler nicht weit genug und so wurde gefordert, man möge doch bitte die große Axt herausholen und JavaScript von Grund auf reformieren. Das ist allerdings nicht ganz so einfach wie es sich anhört. Schon Version 4 von ECMAScript sollte mit allerlei neuen Features aufwarten, doch zwischen den verschiedenen Interessengruppen kam es nicht zur Einigung. Nach Meinung Einiger uferte das Updatepaket aus und man konnte sich nicht darüber verständigen, wie weit und wie schnell man die Entwicklung vorantreiben sollte.

Als klar wurde, dass es sehr Unterschiedliche Auffassungen über die Zukunft von ECMAScript gab, schwiegen sich die an der Arbeitsgruppe beteiligten Partien zunächst ein Jahr lang gegenseitig an. Da sich aber auch dieser Zustand nicht als besonders vielversprechend heraussetellte, einigte man sich schließlich darauf, die Entwicklung der Sprache in kleineren Schritten voranzutreiben. Zunächst wurde mit ECMAScript 5 die Sprache vor allem ausgemistet. Die wenigen neuen Features waren entweder schon praxiserprobt (z.B. Function.prototype.bind) oder wurden mit viel Mühe so konzipiert, dass sie unter keinen Umständen mit bestehenden Scripts im Web kollidieren konnten (z.B. Strict Mode, Objekterweiterungen).

Mit ES5 wurde ein Fundament gelegt, aus das zukünftige Versionen der Sprache aufsetzen konnten, was mit ES6 nun langsam Realität wird. Zwar kommt die Anzahl der Neuerungen für die sechste Version der Sprache nicht an das heran, was mit ES4 geplant war, aber für Web-Verhältnisse kann sich der Umfang des Gebotenen durchaus sehen lassen.

Ein Rüstprogramm für JavaScript

ES6 ist ein vergleichsweise großes Rüstprogramm für JavaScript. Das Standardisierungskomitee um Brendan Eich war teilweise durchaus willens, auch radikalere Umbauten vorzunehmen und so zum Beispiel Köpfe von Kontrollstrukturen ohne Klammern zuzulassen:

if foo > 42 {
    bar++;
}

Obwohl diese eine Änderung aller Voraussicht nach nun nicht den Weg in den Standard finden wird, demonstriert sie doch sehr schön die Geisteshaltung, mit der an das Projekt ES6 herangegangen wurde: JavaScript sollte gründlich um- und aufgebaut werden! Letztlich sind es wesentlich mehr Aus- als Umbauten geworden; bestehende Scripts samt und sonders nicht mehr lauffähig zu halten, war dann doch etwas viel verlangt. Die komplette Liste der Features, die sich (ziemlich sehr) sicher im neuen Standard wiederfinden werden gibt es im ECMAScript-Wiki. Einige Highlights:

Wie immer bei Webstandards müssen wir natürlich auch das, was auf dem Papier steht, mit dem vergleichen, was in der Realität existiert. Dort ist zwar noch nicht viel von dem neuen Standard angekommen, doch das was schon da ist, kann sich in aller Regel sehen lassen.

Status und Unterstützung

ECMAScript 6 selbst ist Stand Anfang 2012 noch kein fertiger Standard, aber die meisten Teile der Spezifikationen sind schon recht stabil. Anders als bei anderen Webstandards funktioniert JavaScript wirklich fast genau so, wie man es von einem Standard erwartet: erst kommt das Papier, dann folgen die Implementierungen. So gibt es verglichen mit den ebenfalls unfertigen Standards HTML5 und CSS3 bisher nur punktuelle Browserunterstützung.

In Chrome Canary, einem experimentelle Chrome-Build, findet man (Stand Anfang 2012) von allen Browsern die meisten ES6-Features. Um in den Genuss von ES6 zu kommen, muss man unter about:flags den Punkt „Enable Experimental JavaScript“ aktivieren:

Der ES6-Aktivierungs-Dialog in Chrome

Danach stehen in allen Scripts, die im Strict Mode ausgeführt werden, die in Chrome Canary vorhandenen ES6-Features zur Verfügung. Auch in anderen V8-basierten Umgebungen kommt man in den Genuss der Features von Chrome – in Node.js lassen sich verschiedene ES6-Aspekte per Startparameter aktivieren (z.B. --harmony_weakmaps für Weak Maps).

Manche JS-Engines stellen die Neuheiten auch ohne zusätzliche Arbeit bereit, so zum Beispiel der Firefox. Es ist realistischerweise davon auszugehen, dass ich all diese Details zur Aktivierung von ES6 im Wochentakt ändern werden, doch vom Experimentieren mit den neuen Features muss das nicht abhalten – denn diese selbst sind schließlich weitgehend stabil.

Problematisch ist allein die fehlende Dokumentation. Neben dem Standard selbst gibt zur Zeit so gute wie gar keine Quellen, aus denen man Infos über ES6 beziehen könnte (z.B. Browser-Sourcecode und dazugehörige Unit Tests), aber genau dafür ist schließlich diese Artikelserie da.

Wie geht es weiter?

Dieser Artikel ist der Auftakt zu einer Serie, die die neuen Features von ES6 genauer beleuchtet. Zur Vorbereitung empfehle ich, die Artikelserie zu ES5 zu lesen – es ist schließlich immer sinnvoll, den ersten Schritt vor dem Zweiten zu tun. Die Serie wird nach und nach neue Teile erhalten wenn immer mehr ES6-Features in den Browsern verfügbar werden, wobei als erstes Thema voraussichtlich Block Scope und Konstanten auf der Agenda stehen – irgendwann in den nächsten Tagen.

Webapp-URLs mit der HTML5 History API

Veröffentlicht am 14. Februar 2012

Vor langer Zeit schrieb ich einen Artikel namens href ist niemals #. In diesem wandte ich mich gegen die damals grassierende Unsitte, Links zu bauen, die außer einem Onlick-Handler nur das Attribut href="#" tragen. Das ist aus gleich zwei Gründen suboptimal: zum einen sollten Ressourcen im Web immer durch eine URL identifizierbar sind und zum anderen fehlt #-Links ein Alternativ-Dokument für Besucher ohne JavaScript. Nun hat sich aber seit dem Aussterben der Dinosaurier einiges getan und heute kann es unter Umständen durchaus sinnvoll sein, Webapps zu schreiben, die JS zwingend voraussetzen – der HTML5-Revolution sei dank! In diesem Zusammenhang erreichte mich die Frage, was man denn nun für JavaScript-Links anstelle von # als URL verwenden sollte. Wie bringt man einer JS-App URLs bei? Mit der HTML5 History API ist das kein (großes) Problem.

Ziel und Ausgangslage

Das Ziel ist im diesem Beispiel, eine Webapp zu bauen, die drei durch JavaScript definierte Zustände hat: entweder steht in einem <p>-Element der Text „Foo“ oder „Bar“ oder das Element ist leer. Mittels Links soll zwischen den Zuständen hin- und hergeschaltet werden können. Üblicherweise würde man diese Webapp, die allein aus der Datei index.html besteht, in etwa wie folgt bauen:

<!doctype html>
<title>FooBar Beta 2.0</title>

<p>
    <a onclick="foo()" href="#">Foo-Zustand</a>
    <a onclick="bar()" href="#">Bar-Zustand</a>
</p>

<p id="content"></p>

<script>
    var content = document.getElementById('content');
    var foo = function(){
        content.innerHTML = 'Foo';
        return false;
    };
    var bar = function(){
        content.innerHTML = 'Bar';
        return false;
    };
</script>

Die Links lassen sich nun zwar anklicken um zwischen „Foo“ und „Bar“ hin- und her zu schalten, aber es fehlen eben die URLs für die beiden Zustände und auch der Zurück-Button des Browsers macht nicht das, was er soll. Das wollen wir ändern.

URLs für die Zustände

Wenn wir unsere beiden Links mit echten URLs ausstatten …

<p>
    <a onclick="foo()" href="foo">Foo-Zustand</a>
    <a onclick="bar()" href="bar">Bar-Zustand</a>
</p>

… ändert sich zunächst nichts, denn wenn die Links angeklickt werden, werden nur die Onclick-Handler aktiv. Die URLs selbst funktionieren nicht, denn unter diesen Adressen sind natürlich keine HTML-Seiten zu finden. Und das soll auch so bleiben; foo und bar sollen schließlich beide keine eigenen Dokumente sein, sondern unterschiedliche Zustände für index.html repräsentieren. Also leiten wir einfach die URLs für die beiden Zustände auf unsere App-Datei um:

RewriteEngine on
RewriteRule foo$ index.html [L]
RewriteRule bar$ index.html [L]

Nun führen alle URLs auf unsere Webapp. Um die App die beiden Zustände repräsentieren zu lassen, müssen wir nur noch die jeweilige URL auslesen und den entsprechenden Inhalt anzeigen:

// Beim laden der Seite die URL parsen und den richtigen Inhalt anzeigen
window.addEventListener('load', function(){
    // Letzte drei Zeichen der URL sind entweder "foo" oder "bar"
    var zustand = window.location.href.substr(-3);
    // Je nach URL mit Foo- oder Bar-Zustand starten
    if(zustand === 'foo'){
        foo();
    }
    else if(zustand === 'bar'){
        bar();
    }
}, false);

Wenn wir nun noch die onlick-Handler aus den Links entfernen …

<p>
    <a href="foo">Foo-Zustand</a>
    <a href="bar">Bar-Zustand</a>
</p>

funktioniert die App inklusive URLs! Das ist allerdings auch kein Wunder, denn im Prinzip haben wir die App einfach de-ajaxifiziert. Jeder Klick auf die Links lädt jetzt die Seite neu, die dann wiederum die URL ausliest und per JavaScript den richtigen Content lädt. Wie re-ajaxifizieren wir nun die App so, dass sie sowohl URLs benutzt als auch nicht ständig neu lädt? Hier kommt die History API ins Spiel.

Das History-Script

Die meisten werden wissen, dass man die Browser-History per JavaScript ansprechen kann um zum Beispiel mit window.history.back() die Funktionalität eines Zurück-Buttons nachzubilden. Seit einiger Zeit erlauben moderne Browser nicht nur das Auslesen der History, sondern auch deren Manipulation. Wie immer bietet MDN einen detaillierten und gleichzeitig verbraucherfreundlichen Überblick, aber ganz kurz gesagt bietet die History-API folgende neuen Features:

  • mit window.history.pushState() kann man einen neuen Eintrag in der Browser-History anlegen. Der Funktion übergibt man ein Status-Objekt, einen Titel und eine URL, die den neuen Status repräsentiert
  • window.history.replaceState() ersetzt den aktuellen History-Eintrag, statt einen neuen anzulegen
  • Das auf window feuernde popstate-Event teilt mit, wenn der aktuelle History-Eintrag wechselt, also wenn zum Beispiel der Nutzer auf den Zurück-Button klickt und damit einen Schritt in der History zurück geht.

Wenn wir nun möchten, dass unsere Links beim Anklicken nicht nur den Zustand ändern, sondern auch neue History-Einträge anlegen, ist das mit window.history.pushState() ein Kinderspiel:

// Beim Link-Klick den richtigen Inhalt anzeigen und die History anpassen
document.querySelector('a[href=foo]').addEventListener('click', function(evt){
    evt.preventDefault(); // Dies verhindert den "normalen" Aufruf der Link-URL
    history.pushState(null, '', 'foo'); // Neuer History-Eintrag "foo"
    foo();
});
document.querySelector('a[href=bar]').addEventListener('click', function(evt){
    evt.preventDefault(); // Dies verhindert den "normalen" Aufruf der Link-URL
    history.pushState(null, '', 'bar'); // Neuer History-Eintrag "bar"
    bar();
});

Die ersten beiden Argumente von window.history.pushState() sind ein Status-Objekt und der Titel für den Histroy-Eintrag. Beides brauchen wir in unserem Fall nicht; der Titel ist sowieso recht nutzlos und das Status-Objekt, das normalerweise den Zustand der App mit einen History-Eintrag verknüpfen würde (ändert sich der aktuelle History-Eintrag, dann ändert sich auch das Status-Objekt entsprechend) brauchen wir auch nicht. Unser App-Status ist komplett aus der URL abzulesen, also übergeben wir der Funktion nur die jeweiligen URLs foo und bar. Da wir ja schon den Code zum Parsen dieser URL im load-Event haben, brauchen wir diesen nur so umzubauen, dass er auch auf das popstate-Event reagiert.

// Wenn sich die History ändert oder die Seite neu lädt, den
// richtigen Zustand herstellen
var changeState = function(){
    // Letzte drei Zeichen der URL sollten entweder "foo" oder "bar" sein
    var zustand = window.location.href.substr(-3);
    // Je nach URL mit Foo-, Bar- oder Leer-Zustand starten
    if(zustand === 'foo'){
        foo();
    }
    else if(zustand === 'bar'){
        bar();
    }
    else {
        content.innerHTML = '';
    }
};
window.addEventListener('load', changeState, false);
window.addEventListener('popstate', changeState, false);

Der Statuswechsel wird nun nicht nur beim Laden der Seite ausgeführt, sondern auch, wenn sich der Nutzer durch die History bewegt. Es ist beim Umbau einzig darauf zu achten, dass der Zustand neben „Foo“ und „Bar“ nun ja auch via Zurück-Button wieder auf „Leer“ gesetzt werden kann, ansonsten bleibt alles beim Alten. Fertig ist die reine JS-App mit echten URLs und voller History-Funktionalität!

Browser, Tools und Links

Die History API wird von allen vernünftigen aktuellen Browsern unterstützt und ab Version 10 auch vom Internet Explorer, doch die einzelnen Implementierungen unterscheiden sich auf recht lästige Art und Weise. So feuern Beispielsweise einige Browser das popstate-Event beim Laden der Seite oder wenn sich der Hash ändert, andere nicht. Um sich all diese Probleme vom Leib zu halten und für ältere Browsern keine Extrawürste braten zu müssen, empfiehlt sich der Einsatz von History.js. Hiermit erhält man eine gemeinsame History-API für alle Browser, die sich genau wie das Original verhält, sämtliche Macken repariert und die auch mit allen gängigen JavaScript-Frameworks klarkommt. Weitere Links, die von Interesse sein könnten:

Termine für Februar und März 2012

Veröffentlicht am 30. Januar 2012

Im Februar und März 2012 gibt es für interessierte Webtechnologie-Padawane folgende Möglichkeiten, sich von mir (und anderen) mit frischem Wissen um die neusten Browser-MackenFeatures ausrüsten zu lassen:

  • 27. Februar 2012 in Düsseldorf: Von meiner Seite sind für die JavaScript-Conference ein enthypender HTML5-Realitätscheck und eine etwas ausgedehntere Hands-On-Session zum Thema „HTML5 Offline Apps“ geplant. Bitte einen mobilen Rechenknecht mit fluppendem lokalem Webserver und Englischkenntnisse mitbringen. Anmeldung hier.
  • 28. Februar - 2. März 2012 an mehreren Orten: Die jeweils eintägige Veranstaltung nennt sich „HTML5 - der neue Web-Standard im Überblick“ und besteht aus einem HTML5-Rundumschlag für alle, die noch einen Gesamtüberblick über das Thema suchen. Das Ganze findet an den folgenden Raum-Zeit-Koordinaten statt:
    • 28. Februar: Düsseldorf
    • 29. Februar: Frankfurt (Main)
    • 1. März: München
    • 2. März: Berlin
    Anmeldung hier.
  • 24. März in Köln: Der 29. Multimediatreff ist eine Konferenz mit barcampiger Atmosphäre für kleines Geld. Mein Beitrag wird sich um das Thema Polyfills für HTML5 drehen und eine etwas andere Kampfansage an den IE6 beinhalten. Anmeldung hier.
  • 26. bis 30. März in Hannover: HTML5-Programmierung im Zusammenspiel mit Microsoft-Technologien heißt der Workshop der Microsoft-Karriereplattform MSAmbition, bei dem HTML5 mit besonderen Blick auf die Verwendung in Windows 8 unter die Lupe genommen werden soll. Aus Gründen ist das Anmelde-Formular offline; wer Interesse hat, meldet sich einfach beim Trainigsleiter Omid Shakeri unter omid.shakeri@msambition.de oder unter der Telefonnummer 040 7675 2550.

Keine Zeit? Kein Problem! Auf der Termine-Seite gibt es auch schon ein paar Einträge für spätere Events. Und ansonsten lasse mich auch gern für andere Zeitpunkte mieten. Anruf genügt.

Sublime Text 2 in Ubuntu integrieren

Veröffentlicht am 27. Januar 2012

Sublime Text 2 ist ein dezent überhypter Cross-Platform-Code-Editor, dem aber ein gewisses Potenzial nicht abgesprochen werden kann. Er bietet eine recht angenehme Mischung aus Nerdigkeit, kombiniert mit den Segnungen moderner GUIs und er unterstützt neben allerlei eigens entwickelten Themes und Plugins (derer es reichlich gibt) auch Textmate-Bundles.

Sublime Text 2 in Aktion
Sublime Text 2 mit Soda-Theme

Seitdem er vor ein paar Tagen Zeiteinheiten Autovervollständigungs-Vorschläge implementiert bekommen hat, habe ich ST2 auch angetestet und versucht, ihn etwas besser in Ubuntu zu integrieren. Da mir zumindest letzteres einigermaßen gelungen ist, schreibe ich hier kurz das Erreichte auf, damit ich das nächste Mal weiß, wo ich es nachlesen kann.

Sublime Text 2 wird als einfacher Tarball gereicht, den man nach dem Download einfach irgendwohin entpackt. Daher sind zunächst weder Dash noch Bash in der Lage, das Programm zu lokalisieren. Erste Hilfe bietet das Hinzufügen des Verzeichnisses von ST2 in die entsprechenden Umgebungsvariablen. Hierfür muss man, ausgehend von einem Installationsverzeichnis ~/SublimeText2, einfach die folgende Zeile an die Datei ~/.profile anhängen:

PATH=$PATH:$HOME/SublimeText2

Damit lässt sich der Editor (nach einmaliger Neuanmeldung) schon mal via Terminal oder Alt+F2 starten.

In das Unity-Dash kommt eine Applikation nur, wenn sie in das Gnome-Menü eingetragen ist. In ihrer grenzenlosen Weisheit haben die Ubuntu-Entwickler allerdings darauf verzichtet, den Menü-Editor alacarte standardmäßig zu installieren, so dass wir das nachholen dürfen:

sudo apt-get install alacarte && alacarte

Den eigentlichen Eintrag anzulegen ist dann aber einfach: in das Untermenü „Entwicklung“ wechseln, „New Item“ klicken, Pfad und Namen angeben, fertig! Wichtig ist nur, hinter den Pfad zur Applikation %f anzuhängen ‐ das sagt dem System, dass das Programm auch für Start via Doppelklick auf eine unterstützte Datei geeignet ist. In meinem Fall lautet der vollständige Pfad also /home/peter/SublimeText2/sublime_text %f

Dialoge zum Anlegen eines Menüeintrags für Sublime Text 2

Schon steht das Programm korrekt bezeichnet und bebildert im Dash. Als Icon verwende ich diese Datei, die ich vor ein paar Tagen irgendwo aus Dschungel der diversen ST2-Webseiten gefischt habe. Wer es eher fancy mag, findet im Netz entsprechende Alternativen, aber mir gefällt es schlicht ganz gut im Dash:

Sublime Text 2 im Ubuntu-Dash

Mal sehen ob sich Sublime Text 2 als tauglich für mich erweist. Seit ich schwerpunktmäßig Webtechnologie-Erklärbär bin, baue ich ja hauptsächlich Prototypen und Kleinst-Projekte, da ist ein möglichst schlanker Editor ohne größere IDE-Ambitionen das Mittel der Wahl. Und da ich auf der anderen Seite auch kein komplett vernerdeter Emacs/Vim-Höhlenmensch bin, könnte das passen. Schauen wir mal …