Der kommende Sprachstandard ECMA-Script 6 beseitigt viele historische Javascript-Eigenheiten. Die bisher vermissten Klassen, Module und Default-Parameter erleichtern das Coden vor allem den Umsteigern von anderen Programmiersprachen.
Das früher belächelte Javascript bekam in den letzten Jahren mächtig Auftrieb. Doch immer noch erschweren die historisch bedingten Besonderheiten [1] der Skriptsprache das Programmieren. Beispielsweise besitzt Javascript nur einen einzigen Namensraum, in dem globale Variablen kollidieren können. Die Sondervariable »this« ist nicht lexikalisch gebunden und verweist in Rückruffunktionen meist auf ein anderes Objekt. Zudem verstehen die wenigsten die ungewöhnliche, weil Prototypen-basierte Vererbung. ECMA-Script, die standardisierte Variante, schafft in Version 6 mit vielen Neuerungen Abhilfe. Auch wenn sie bisher nur als Entwurf [2] vorliegt und voraussichtlich erst 2015 veröffentlicht wird, hat die Umsetzung in den Browsern bereits begonnen.
Der vorliegende Artikel bietet einen Überblick über die Neuerungen in ECMA-Script 6 anhand einer Beispielanwendung. Das Metronom für den Browsereinsatz [3] hat der Autor mit ECMA-Script 6, dem Traceur-Compiler, HTML 5, dem Web-Audio-API und CSS 3 umgesetzt.
Den Stand der Implementierung von ECMA-Script 6 in aktuellen Browsern sowie dem Server Node.js zeigt Tabelle 1, abgeleitet von der umfangreichen Kompatibilitäts-Matrix unter [4]. Eine aussagekräftigere Beschreibung von ECMA-Script 6 in Firefox findet sich in der Mozilla-Dokumentation [5]. Als Lückenbüßer für fehlende oder nicht korrekt implementierte Features fungiert in Firefox, Chrome und Internet Explorer ab Version 10 der Traceur-Compiler [6].
Tabelle 1
Unterstützung von ECMA-Script 6
|
ECMA-Script-6-Features |
Firefox 33 |
Chrome 37 |
Internet Explorer 11 |
Node.js |
|---|---|---|---|---|
|
Arrow-Funktionen |
ja |
nein |
nein |
nein |
|
Konstanten |
ja |
ja |
ja |
ja |
|
Block-Scope |
ja |
ja |
ja |
ja |
|
Klassen |
nein |
nein |
nein |
nein |
|
Module |
nein |
nein |
nein |
nein |
|
For-of-Schleife |
ja |
nein |
nein |
nein |
|
Iteratoren |
ja |
nein |
nein |
nein |
|
Generatoren |
ja |
ja |
nein |
ja |
|
Rest-Parameter |
ja |
nein |
nein |
nein |
|
Comprehensions |
ja |
nein |
nein |
nein |
|
Template-Strings |
nein |
nein |
nein |
nein |
|
Destructuring Assignment |
ja |
nein |
nein |
nein |
Traceur übersetzt Programmcode von ECMA-Script 6 zurück in den nativen Javascript-Code der Vorgängerversion ECMA-Script 5. Mit Traceur programmiert beispielsweise das Open-Source-Projekt Angular JS bereits die nächste Version seines Javascript-Frameworks [6]. Traceur wird seit 2011 von Google unter Apache License 2.0 entwickelt. Derzeit liegt der Compiler in Version 0.0.46 vor.
Traceur-Compiler
Listing 1 zeigt die Installation von Traceur für den lokalen Einsatz auf einem Server unter Ubuntu 12.04. Da Traceur selbst in Javascript umgesetzt ist, installiert Zeile 1 zunächst mit dem Paketmanager Apt die Server-seitige Javascript-Implementierung Node.js. Zeile 2 fügt anschließend Nodes Paketmanager Npm hinzu.
Listing 1
Traceur-Installation
01 $ sudo apt-get install nodejs 02 $ sudo apt-get install npm 03 $ npm config set strict-ssl false 04 $ wget https://github.com/google/traceur-compiler/archive/0.0.46.tar.gz 05 $ tar xzvf 0.0.46.tar.gz 06 $ cd traceur-compiler 07 $ npm install commander
Damit Zeile 7 das Node.js-Modul Commander nachziehen kann, erlaubt Zeile 3 zunächst Npm, Ubuntus selbst unterzeichnetes SSL-Zertifikat zu verwenden. Die Zeilen 4 und 5 laden und entpacken die aktuelle Release von Traceur. Abbildung 1 zeigt das Terminal nach der erfolgreichen Installation.
Mit Hilfe des Befehls »traceur« lässt sich nun im Wurzelverzeichnis der Release ECMA-Script 6 nach Version 5 zurückübersetzen:
./traceur --out ecma5.js --script ecma6.js
Listing 2 demonstriert den Einsatz von Traceur im so genannten On-the-fly-Modus in einem HTML-Dokument. Dabei übersetzt der Compiler ECMA-Script vor dem Ausführen durch den Browser in natives Javascript. Das Script-Tag in Zeile 4 lädt den Traceur-Compiler, jenes in Zeile 5 das dazu benötigte Modul »bootstrap.js« aus dem Netz. Die Anweisung in Zeile 6 aktiviert alle Features von Traceur. Im anschließenden Script-Tag veranlasst das Attribut »type« mit dem Wert »module« Traceur dazu, den Javascript-Code innerhalb des Tag zu übersetzen.
Listing 2
Traceur einbinden
01 <!DOCTYPE html> 02 <html> 03 <head> 04 <script src="https://traceur-compiler.googlecode.com/git/bin/traceur.js"></script> 05 <script src="https://traceur-compiler.googlecode.com/git/src/bootstrap.js"></script> 06 <script>traceur.options.experimental = true;</script> 07 <script type="module">(() => console.log(123))()</script> 08 </head> 09 <body></body> 10 </html>
Ein Dokument wie in Listing 2 kann der Entwickler einsetzen, um ECMA-Script-6-Code und Traceur zu testen. Dazu fügt er Code in das Script-Tag in Zeile 7 ein und lädt das Dokument anschließend in aktuellen Versionen von Firefox oder Chrome. Die Ergebnisse lassen sich in der Javascript-Konsole des Browsers ablesen, sie öffnet sich bei beiden Browsern durch die Tastenkombination [Strg]+[Shift]+[J].
Um Traceur on the Fly zu testen, braucht der Rechner allerdings Internetanbindung. Unter Chrome muss selbst das HTML-Dokument von einem Webserver kommen, und sei es ein lokaler. Abbildung 2 zeigt die Ausgabe von Listing 2 in der Konsole des Chrome-Browsers.

Listing 2 schreibt in Zeile 7 den Text »123« auf die Konsole.” width=”300″ height=”111″ />
Abbildung 2: Der Javascript-Code aus Listing 2 schreibt in Zeile 7 den Text »123« auf die Konsole.Neue Sprachmittel
ECMA-Script 6 erfindet kaum etwas neu, sondern greift auf Bewährtes aus anderen Sprachen zurück. Beispielsweise führt die neue Version mit den so genannten Arrow-Funktionen die unter anderem aus Haskell bekannten Lambda-Funktionen ein. Sie verkürzen das Schreiben der Funktionsdefinition und binden die Sondervariable »this« lexikalisch. In dem Ausdruck
[2,4,6,8].map(x => x+1)
beschreibt »x => x+1« eine Rückruffunktion, die der Programmierer bisher nur mit »function(x) {return x+1}« definieren konnte.
Ebenfalls neu sind die aus C vertrauten Konstanten. Die Definition von Konstanten leitet in ECMA-Script 6 das Schlüsselwort »const« ein, etwa »const c = 7;« . Der Wert von Konstanten lässt sich wie in C nicht überschreiben.
Das Schlüsselwort »let« zu Beginn einer Variablendefinition, beispielsweise »let x = 2;« , schränkt die Sichtbarkeit der Variablen auf den umgebenden Block ein. Listing 3 zeigt in der ersten Zeile Let im Kopf einer For-Schleife. Außerhalb der Schleife ist die mit Let definierte Variable »x« undefiniert. Stünde anstelle von »let« dort das Schlüsselwort »var« , hätte »x« in Zeile 4 den Wert 1.
Listing 3
Block-Scope let-statement.html
01 for(let x=0; x<2; x++) {
02 console.log(x)
03 }
04 console.log(x);
Klassenbewusst
ECMA-Script 6 bietet erstmals die Möglichkeit, Objekte ähnlich wie in Java mittels einer Klasse zu erzeugen. Listing 4 zeigt die Definition zweier Klassen nach dem neuen Standard. Das Schlüsselwort »class« leitet in Zeile 1 die Klassendefinition für die Klasse »Creature« ein. Es folgt der Klassenname, der Körper ist von geschweiften Klammern umgeben. Er enthält in den Zeilen 2 bis 5 die Konstruktor-Funktion der Klasse. Diese kommt beim Erzeugen eines Objekts, etwa mit »new Creature(‘Bob’, ‘friendly’)« , zur Ausführung. Die Konstruktor-Funktion weist in den Zeilen 3 und 4 den Attributen »name« und »age« die Werte der gleichnamigen Aufrufparameter aus Zeile 2 zu.
Listing 4
Klassen class-statement.html
01 class Creature {
02 constructor(name, age) {
03 this.name = name;
04 this.age = age;
05 }
06 }
07
08 class Human extends Creature {
09 constructor(name, age, character) {
10 super(name, age);
11 this.character = character;
12 }
13
14 toString() {
15 return `${this.name} (${this.age}) is a ${this.character} human`;
16 }
17 };
18
19 console.log((new Human('Bob', 3, 'friendly')).toString());
Vererbung
Die Definition der Klasse »Human« (Zeilen 8 bis 17) leitet sich mit Hilfe des Schlüsselworts »extends« von der Klasse »Creature« ab. Die Konstruktor-Funktion von »Human« ruft in Zeile 10 mittels »super()« -Funktion die Konstruktor-Funktion der Elternklasse auf. Dadurch erben Instanzen von »Human« die Eigenschaften »name« und »age« .
»Human« enthält zudem die Methode »toString()« . Deren Definition beginnt (Zeile 14) direkt mit ihrem Namen, gefolgt von einer leeren Parameterliste, und schließt mit dem Funktionsblock in geschweiften Klammern. »toString()« erstellt eine Repräsentation des Objekts als Zeichenkette. Dazu nutzt die Methode in Zeile 15 einen String-Literal-Ausdruck.
String-Literals sind ebenfalls neu in ECMA-Script 6. Umschlossen von so genannten Backticks erfolgt in ihnen – ähnlich wie in der Bash – die Parameter-Expansion in der Form »${Variablenname}« . Zum Test wertet Zeile 19 den Ausdruck »(new Human(‘Bob’, 3, ‘friendly’)).toString()« aus. Das Ergebnis »Bob (3) is a friendly human« schreibt die Methode »log()« auf die Browserkonsole.
Ähnlich wie bei Node.js kann der Entwickler mit ECMA-Script 6 Funktionalität in Module auslagern. Zunächst zeigt Listing 5 ein Modul. Das Schlüsselwort »export« veranlasst das Modul, in Zeile 2 die Variable Pub und in Zeile 3 die Klasse »pubObj« zu exportieren. Die Variable »priv« aus Zeile 1 dagegen bleibt intern. Auf sie kann nur das Modul selbst zugreifen, etwa in Zeile 5.
Listing 5
Modul export-statement.js
01 var priv = 0;
02 export var pub = 3;
03 export class pubObj {
04 constructor(val) {
05 this.inc = val + ++priv;
06 }
07 }
Module importieren
Listing 6 zeigt, wie der Programmierer ein Modul benutzt. Das Schlüsselwort »import« erledigt in Zeile 1 das Einbinden. Die folgende, in geschweifte Klammern gefasste und kommaseparierte Liste bindet die Variable »pub« aus dem Modul an die Variable »a« sowie die Klasse »pubObj« an »Obj« . Die Referenzierung des Moduls erfolgt über dessen Dateinamen ohne die ».js« -Endung.
Listing 6
Importieren import-statement.html
01 import {pub as a, pubObj as Obj} from 'export-statement';
02 console.log((new Obj(1 + a)).inc);
03 console.log((new Obj(2 + a)).inc);
04 console.log(priv);
Der Konstruktor »new« in Zeile 2 von Listing 6 übergibt den Wert 4, jener in der nächsten Zeile den Wert 5 an die Konstruktor-Funktion aus Listing 5. Aus beiden instanzierten Objekten liest das Skript das Attribut »inc« aus. Das Attribut ergibt sich aus der Summe des an die Konstruktor-Funktion übergebenen Werts und dem Wert der privaten Variablen »priv« aus Listing 5. Da »priv« vor der Addition um 1 inkrementiert wird, schreibt der Code zunächst 5 und dann 7 auf die Browserkonsole.
Mit »Map« , »WeakMap« , »Set« und »WeakSet« bietet ECMA-Script 6 dem Programmierer vier neue Objekte zum Speichern von Daten. »Map« und »WeakMap« ähneln den Javascript-Objekten, denn sie speichern ebenfalls Schlüssel-Wert-Paare. Anders als herkömmliche Objekte sind Map-Objekte aber iterierbar. Zudem akzeptieren sie einen beliebigen Datentyp als Schlüssel. Der Unterschied zwischen »Map« und »WeakMap« liegt in der Speicherverwaltung: Nur in »WeakMap« -Objekten löscht der Garbage Collector Referenzen, die ihre Lebensdauer überschritten haben.
»Set« und »WeakSet« hingegen ähneln mathematischen Mengen. Sie speichern einen Wert oder eine Referenz nur einmal ab. Ein erneutes Hinzufügen bleibt folgenlos. »WeakSet« -Objekte behandelt der Garbage Collector wie »WeakMap« -Objekte. Auch »Set« – und »WeakSet« -Objekte sind iterierbar. Um sie einfach auslesen zu können, bietet ECMA-Script 6 die neu geschaffene For-of-Schleife. Da Traceur die vier oben besprochenen Objekte leider nicht unterstützt, iteriert diese Schleife in Listing 7 über ein Feld, denn auch Felder sind in ECMA-Script 6 von Haus aus iterierbar. Die For-of-Schleife schreibt in Zeile 2 die Werte 1, 3 und 5 auf die Browserkonsole.
Listing 7
For-of-Schleife for-of-statement.html
01 for (let x of [1, 3, 5]) {
02 console.log(x);
03 }
Iterator-Protokoll
Den Zugang zu iterierbaren Objekten findet die For-of-Schleife über das Iterator-Protokoll. Es erinnert wie viele andere Neuerungen stark an Python [8]. Um den Entwicklern die Implementierung des Iterator-Protokolls abzunehmen, bietet das neue ECMA-Script Generatorfunktionen. Diese ähneln formal konventionellen Funktionen, erzeugen aber das Iterator-Objekt. In Listing 8 iteriert die For-of-Schleife (Zeilen 8 bis 10) über den Aufruf der Generatorfunktion »sum« (Zeilen 1 bis 6). Auch die Definition dieser Generatorfunktion leitet das Schlüsselwort »function« ein. Der Sternchen-Operator unmittelbar vor ihrem Namen weist die Funktion als Generator aus.
Listing 8
Generator generator-functions.html
01 function *sum(array) {
02 var sum = 0;
03 for (var x of array) {
04 yield sum += x;
05 }
06 }
07
08 for (let x of sum([1, 2, 3, 4])) {
09 console.log(x);
10 }
Innerhalb dieser Funktion speichert die gleichnamige Variable »sum« ein Zwischenergebnis, Zeile 2 initialisiert sie mit 0. Die For-of-Schleife iteriert in den Zeilen 3 bis 5 über das dem Generator übergebene Feld, Zeile 4 addiert den aktuellen Feldwert zum Zwischenergebnis. Das Schlüsselwort »yield« veranlasst die Rückgabe des aktualisierten Zwischenergebnisses an den nächsten Durchlauf der For-of-Schleife. Für den nächsten Schleifendurchlauf setzt der Generator nach »yield« wieder ein.
Die For-of-Schleife macht sich auch in den so genannten Comprehensions (Mengendefinitionen) nützlich. Die Array-Comprehensions sind eine Möglichkeit, ganze Felder mit einem einzigen kompakten Ausdruck zu erzeugen. Der Ausdruck
[for (x of [2, 3]) for(y of [2, 3])Math.pow(x, y)]
beispielsweise generiert das Feld »[4, 8, 9, 27]« durch die verschachtelte Ausführung beider For-of-Schleifen über den Ausdruck »Math.pow(x, y)« . Verwendet der Programmierer umschließende runde Klammern, kann er mit Generator-Comprehensions iterierbare Objekte erzeugen. Über den Ausdruck
(for (x of [2, 3]) Math.pow(x, 2))
iteriert eine For-of-Schleife über die Werte 4 und 8. Ebenso kompakt und nützlich wie die Comprehensions sind so genannte Destructuring Assignments. Mit diesen besonderen Zuweisungen lassen sich in einem Ausdruck Objekte und Felder mit einfacher Syntax auslesen und gleichzeitig an mehrere Variablen binden. Die Anweisung »[b, a] = [a, b]« etwa vertauscht die Werte der beiden Variablen. Die Anweisung
var [a,,[b,,[c],d]] =[1, 0, [2, 0, [3, 0], 4], 0]
initialisiert die Variablen »a« bis »d« mit den Werten 1, 2, 3 und 4. Der Ausdruck links des Gleichheitszeichens legt sich wie eine Maske über das Feld rechts, überspringt Stellen mittels Komma und folgt dem Feld mittels eckiger Klammern in die Tiefe. Der ECMA-Script-Code
var {A: a, B: {C: b}} ={A: 1, B: {C: 2}, D: 3}
initialisiert analog die Variablen »a« mit 1 und »b« mit 2. Solche Destructuring Assignments kann der Programmierer auch in den Parameterlisten von Funktionen und in Schleifen verwenden.
Javascript ab ECMA-Script 6 kennt wie PHP Defaultparameter in der Parameterliste von Funktionsdefinitionen. Beim Aufruf der Funktion
function params(a=0, b=0){ return a+b}
initialisiert die Sprache »a« und »b« mit 0, falls keine Werte angegeben sind.
Zum Verarbeiten langer Parameterlisten bietet ECMA-Script 6 den Spreadoperator »…« . Der Aufruf »rest(1, 2, 3, 4, 5)« der Funktion »function rest(a, b, …rest){}« veranlasst Javascript dazu, die Werte 3, 4 und 5 in das Feld namens »rest« zu packen. Der Spreadoperator lässt sich auch zum Entpacken von Feldern verwenden. In den Ausdruck »[1, 2, …rest, 6]« fügen sich dabei die Werte des Feldes »rest« ein, sodass das Resultat »[1, 2, 3, 4, 5, 6]« lautet.
Neben den bisher besprochenen Neuerungen bringt ECMA-Script 6 noch eine Vielzahl neuer Methoden und einige neue Objekte, beispielsweise das »Promise« -Objekt. Eine vollständige Beschreibung würde allerdings den Rahmen dieses Artikels sprengen.
Beispielanwendung
Der Programmcode der folgenden Beispielanwendung fällt dank ECMA-Script 6 einfacher aus als früher, lässt sich leichter lesen und hilft somit Programmierfehler zu vermeiden. Das gilt insbesondere für die Klasse in Listing 11. Die Definition ihrer Methoden erfolgt inline, also im Klassenkörper. Das spart Programmcode und macht es leichter, die Methoden der Klasse zuzuordnen.
Listing 11
Modul metronome/metronome.js
01 export class Metronome {
02 constructor() {
03 this.context = new (window.AudioContext || window.webkitAudioContext)();
04 this.loadBuffer();
05 }
06
07 loadBuffer(url="pulse.ogg") {
08 var request = new XMLHttpRequest();
09 request.open("GET", url, true);
10 request.responseType = "arraybuffer";
11 request.onload = () => this.context.decodeAudioData(request.response, (buffer) => this.buffer = buffer);
12 request.send();
13 }
14
15 play(beats) {
16 this.stop();
17 this.src = this.context.createBufferSource();
18 this.src.buffer = this.extendBuffer(beats, this.buffer);
19 this.src.loop = true;
20 this.src.connect(this.context.destination);
21 this.src.start();
22 }
23
24 stop() {
25 ("src" in this)?this.src.stop():"";
26 }
27
28 extendBuffer(beats, buffer) {
29 var total = 60/beats * buffer.sampleRate;
30 var ext = this.context.createBuffer(buffer.numberOfChannels, total, buffer.sampleRate);
31 for(let i=0; i<buffer.numberOfChannels; i++)
32 ext.getChannelData(i).set(buffer.getChannelData(i));
33 return ext;
34 }
35 }
Die verwendete Syntax erleichtert zudem Java- oder C++-Programmierern den Einstieg in die ansonsten eher kryptische prototypale Vererbung in Javascript. Auch der Einsatz von Arrow-Funktionen in der Definition der beiden Rückruffunktionen in Zeile 11 vereinfacht den Code: Arrow-Funktionen schreiben sich in einer einzigen Zeile und binden die Sondervariable »this« lexikalisch an das aktuelle Objekt. Damit entfällt ein weiteres Zuordnungsproblem.
Abbildung 3 zeigt die Beispielanwendung in Firefox unter Ubuntu 12.04. Das Metronom spielt einen Taktschlag in einer Schleife. Das gewünschte Metrum trägt der Anwender im Zahlenfeld links ein. Die beiden Buttons rechts davon starten und stoppen das Metronom.
Die hörbaren Taktschläge erzeugt das Web-Audio-API [9]. Diese Schnittstelle generiert und verarbeitet Audiodaten mittels Javascript im Browser und leitet sie an das Soundsystem des Rechners weiter. Es existiert derzeit zwar nur als W3C-Entwurf, aber die aktuelleren Versionen der Browser Firefox und Chrome verfügen bereits über brauchbare Implementierungen.
HTML-Gerüst
Das HTML-Dokument der Beispielanwendung zeigt Listing 9. In seinem Kopf (Zeilen 3 bis 10) bindet Zeile 4 Formatierungsanweisungen per CSS-Datei ein. Die Zeilen 5 und 6 laden den Traceur-Compiler, Zeile 7 das Open-Source-Javascript-Framework Jquery [10]. Zeile 9 integriert schließlich den Javascript-Code der Beispielanwendung. Zeile 8 schaltet alle verfügbaren Features von Traceur frei. Der Div-Container nimmt in den Zeilen 12 bis 19 neben dem Titel im H1-Element die Liste mit den Steuerelementen (Zeilen 14 bis 18) auf. Das Eingabefeld in Zeile 15 ist vom Typ »number« , die Knöpfe vom Typ »button« .
Listing 9
HTML-Dokument metronome/index.html
01 <!DOCTYPE html> 02 <html> 03 <head> 04 <link href="app.css" rel="stylesheet"></link> 05 <script src="https://traceur-compiler.googlecode.com/git/bin/traceur.js"></script> 06 <script src="https://traceur-compiler.googlecode.com/git/src/bootstrap.js"></script> 07 <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 08 <script>traceur.options.experimental = true;</script> 09 <script src="app.js" type="module"></script> 10 </head> 11 <body> 12 <div id="container"> 13 <h1>Metronom</h1> 14 <ul> 15 <li><input id="beats" type="number" value="96"/></li> 16 <li><input id="start" type="button" value="Start"/></li> 17 <li><input id="stop" type="button" value="Stop"/></li> 18 </ul> 19 </div> 20 </body> 21 </html>
Listing 10 enthält den ECMA-Script-6-Code. Die erste Zeile importiert die Klasse »Metronome« , Zeile 2 definiert die Variable »met« zum Speichern einer »Metronome« -Instanz. In der folgenden Zeile erzeugt und speichert ein Jquery-Ausdruck eine Instanz von »Metronome« .
Listing 10
Anwendung metronome/app.js
01 import {Metronome} from 'metronome';
02 var met;
03 $(document).ready(() => met = new Metronome())
04 $('#start').click(() => met.play($('#beats').val()));
05 $('#stop').click(() => met.stop());
Die Ereignisbehandlung in Zeile 4 reagiert auf einen Klick auf den Start-Knopf, indem sie die Methode »play()« aufruft und das Metronom damit startet. Der Jquery-Ausdruck »$(‘#beats’).val()« in der Parameterliste des Methodenaufrufs liest zuvor das Metrum aus dem Textfeld mit der ID »beats« (Zeile 15). Ein Klick auf den Stop-Button ruft die Methode »stop()« auf.
Listing 11 zeigt die Klasse »Metronome« . Die Konstruktor-Funktion (Zeilen 2 bis 5) initialisiert in Zeile 3 das Attribut »context« mit dem Audiokontext des Web-Audio-API. Über dieses Attribut ruft ECMA-Script im Folgenden alle Methoden des API auf.
Das Hersteller-Präfix »webkit« berücksichtigt den Browser Chrome. Zeile 4 ruft die Methode »loadBuffer()« (Zeilen 7 bis 13) auf. Sie lädt die Audiodatei mit dem Taktgeräusch vom Server und überführt sie zwecks Weiterverarbeitung in einen Audio-Buffer. Dazu erzeugt und speichert Zeile 8 zunächst ein »XMLHttpRequest« -Objekt in der Variablen »request« . Zeile 9 lädt die Audiodatei mit Hilfe der »open()« -Methode vom Server.
Audioverarbeitung
Die Rückruffunktion in Zeile 11 verarbeitet die geladene Audiodatei weiter: Die Anwendung wandelt sie mit der Methode »decodeAudioData()« in einen Audio-Buffer um und speichert sie im Attribut »buffer« des Objekts. Die Methode »play()« (Zeilen 15 bis 22) spielt den Taktschlag in einer Schleife. Zunächst stoppt sie in Zeile 16 das Abspielen des Taktschlags, indem sie »stop()« aufruft. Anschließend erzeugt Zeile 17 per »createBufferSource()« einen abspielbaren Audioknoten vom Typ »AudioBufferSourceNode« und speichert ihn im Attribut »src« .
Als Input vereinbart Zeile 18 den vom Server geladen Audio-Buffer. Diesen bringt zuvor noch die Methode »extendBuffer()« auf die passende Länge. Zeile 19 veranlasst den abspielbaren Audioknoten zur steten Wiederholung des Audio-Buffers, bevor Zeile 20 ihn mit dem Soundsystem des Rechners verbindet. Der Aufruf der Methode »start« beginnt in Zeile 21 die Wiedergabe. Wie Audio-Buffer, abspielbarer Audioknoten und Soundsystem verschaltet sind, macht Abbildung 4 anschaulich.
Das Taktgeräusch dauert etwa 0,1 Sekunden. Die Methode »extendBuffer()« (Zeilen 28 bis 34) verlängert die Dauer des Taktschlags gemäß dem eingestellten Metrum – bei einem Metrum von 120 Schlägen pro Minute auf 0,5 Sekunden, bei 60 Schlägen auf 1 Sekunde. Dazu berechnet Zeile 29 die geforderte Spieldauer in Einheiten der Samplerate, mit der die Audiodatei des Taktschlags digitalisiert wurde, und speichert diese in der Variablen »total« ab. Das eingestellte Metrum geht über die im Aufruf enthaltene Variable Beats in die Spieldauer ein.
Die anschließende Zeile erzeugt durch den Start der Methode »createBuffer()« einen leeren Audio-Buffer von der zuvor berechneten Spieldauer. Zudem übernimmt der Code die Anzahl der Kanäle und die Samplerate aus dem Audio-Buffer mit dem Taktschlag. Die abschließende For-Schleife kopiert jeden Kanal des Audio-Buffers mit dem Taktschlag jeweils an den Beginn des entsprechenden Kanals des verlängerten Buffers.
Fazit
ECMA-Script 6 renoviert die bislang klassenlose Sprache mit Elementen aus anderen Programmiersprachen. Das vereinfacht und modernisiert sie und dürfte ihrem Erfolg zuträglich sein. Bis die Browser jedoch nativ ECMA-Script 6 sprechen, wird noch einige Zeit vergehen. Unterdessen bietet der Compiler Traceur Entwicklern die Möglichkeit, die Vorteile der modernisierten Skriptsprache schon heute zu nutzen. (mhu)
Infos
- Douglas Crockford, “JavaScript: The Good Parts”: http://www.youtube.com/watch?v=hQVTIJBZook
- ECMA-Script 6 (Entwurf): http://people.mozilla.org/~jorendorff/es6-draft.html
- Beispielanwendung Metronom: http://pamoller.com/metronome
- Juriy Zaytsev, “ECMA-Script 6 compatibility table”: http://kangax.github.io/compat-table/es6/
- Unterstützung von ECMA-Script 6 in Mozilla: https://developer.mozilla.org/en-US/docs/Web/JavaScript/ECMAScript_6_support_in_Mozilla
- Traceur-Compiler: https://github.com/google/traceur-compiler/
- Angular JS 2.0: http://blog.angularjs.org/2014/03/angular-20.html
- Iteratoren in Python 2: https://docs.python.org/2/library/stdtypes.html#iterator-types
- Web-Audio-API: http://webaudio.github.io/web-audio-api/
- Jquery: http://jquery.com
- Listings zum Artikel: https://www.linux-magazin.de/static/listings/magazin/2014/09/ecmascript








