Februar 2016

ECMAScript 6 / ECMAScript 2015

Vorwort

ECMAScript 6 (ES6) ist die neuste Version des ECMAScript Standards, welche im Juni 2015 vom Ecma Komitee TC39 ratifiziert wurde. Dieser Standard ist die offizielle Spezifikation der Programmiersprache, die umgangssprachlich JavaScript genannt wird, und die in jedem Web-Browser ihre Verwendung findet. Im Zuge des langen Standardisierungsprozesses von 6 Jahren wurde entschieden neue Versionen in Zukunft mit kleinerem Umfang zu versehen und jährlich zu veröffentlichen. Daher der neue offizielle Name ECMAScript 2015 (ES2015).

ES6 ist ein signifikantes Update der Sprache und das erste große Update seit ES5 im Jahre 2009. Ziel von ES6 ist eine Verbesserung der Sprache für folgende Punkte:

  • komplexe Webanwendungen
  • Bibliotheken
  • Emulation von Features zukünftiger Versionen

Punkt 1 erkennt an, dass heutige Anwendungen, die in JavaScript geschrieben wurden, teilweise riesig sind. Ein Schlüssel-Feature von ES6 das diesen Punkt verbessert sind Modules. Modules sind auch eine Antwort auf Punkt 2. Mehrere Features wurden außerdem speziell hinzugefügt um es leichter zu machen nach JavaScript zu kompilieren. Beispielsweise Math.fround() und Math.imul(). Beide sind nützlich wenn beispielsweise von C/C++ nach JavaScript kompiliert wird.

Beispiele dieser neuen Features sind: Classes, Modules, Arrow functions, named function parameters. Hier ein Überblick der wichtigsten Features.

Arrow functions & lexical 'this'

Arrow functions sind eine verkürzte Schreibweise welche die => syntax benutzt um functions zu schreiben. Sie unterscheiden sich zu „normalen“ functions insofern als dass der Wert von 'this' innerhalb im lexikalischen Rahmen gilt. Runde Klammern können bei nur einem Parameter weggelassen werden.

                                    // Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

// Lexical this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
};
                                

Classes

ES6 Klassen sind eine vereinfachte Schreibweise des bekannten ES5 prototype class-pattern. Klassen unterstützen Vererbung, 'super' Aufrufe, Instanz- und Statische-Methoden, und Konstruktoren.

                                    class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}
                                

Template Strings

Template strings sind eine vereinfachte Schreibweise um strings zu erzeugen. Sie ähneln denen in Perl und Python, und unterstützen Interpolation.

                                    // Basic literal string creation
`This is a pretty little template string.`

// Multiline strings
`In ES5 this is
 not legal.`

// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// Unescaped template strings
String.raw`In ES5 "\n" is a line-feed.`

// Construct an HTTP request prefix is used to interpret the replacements and construction
GET`http://foo.org/bar?a=${a}&b=${b}
    Content-Type: application/json
    X-Credentials: ${credentials}
    { "foo": ${foo},
      "bar": ${bar}}`(myOnReadyStateChangeHandler);
                                

Destructuring

Destructuring erlaubt variable binding mithilfe von pattern matching, und kann auf Arrays und Objects angewendet werden. Nicht gefundene Werte sind automatisch undefined.

                                    // list matching
var [a, ,b] = [1,2,3];
a === 1;
b === 3;

// object matching
var { op: a, lhs: { op: b }, rhs: c }
       = getASTNode()

// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()

// Can be used in parameter position
function g({name: x}) {
  console.log(x);
}
g({name: 5})

// Fail-soft destructuring
var [a] = [];
a === undefined;

// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;
                                

Default + Rest + Spread

Function parameter die sich dynamisch verhalten, je nachdem welche Werte übergeben wurden.

                                    function f(x, y=12) {
  // y is 12 if not passed (or passed as undefined)
  return x + y;
}
f(3) == 15
function f(x, ...y) {
  // y is an Array
  return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
  return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
                                

Let + Const

Neue Keywords um bindings zu erstellen die Block Sichtbarkeit haben. 'let' als de facto Ersatz für 'var'. 'const' für Konstanten.

                                    function f() {
  {
    let x;
    {
      // okay, block scoped name
      const x = "sneaky";
      // error, const
      x = "foo";
    }
    // okay, declared with `let`
    x = "bar";
    // error, already declared in block
    let x = "inner";
  }
}
                                

Iterators + For..Of

Iterators erlauben die Implementierung eigener Iteration Objects ähnlich wie in C# oder Java.

                                    let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return { done: false, value: cur }
      }
    }
  }
}

for (var n of fibonacci) {
  // truncate the sequence at 1000
  if (n > 1000)
    break;
  console.log(n);
}
                                

Modules

Code kann nun in Modulen strukturiert werden. Eine JavaScript Datei stellt ein Modul dar welches immer asynchron geladen wird. Ähnelt der AMD/Common.js Struktur.

                                    // lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2? = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2? = " + sum(pi, pi));
                                

Fazit

Mit ES6/ES2015 ist es nun wesentlich einfacher komplexe Web-Anwendungen zu schreiben. Alle großen Browser-Hersteller arbeiten zur Zeit daran ES6 zu implementieren und sollten mittelfristig fähig sein nativen ES6 Code auszuführen. Web-Entwickler können jedoch schon heute ihre Anwendungen darin schreiben, da es viele Tools (wie z.B. Babel.js) gibt, die es erlauben die wichtigsten Features in gängigen JavaScript Code zu kompilieren (welcher auf jedem aktuellen Browser läuft).

Benjamin