ECMAScript 6 besser verstehen: Klassen und Vererbung

David Catuhe

David Catuhe ist Principal Program Manager bei Microsoft mit einem Fokus auf...

Hiermit möchte ich Sie zu einer Serie von Artikeln über ECMAScript 6 einladen, mit der ich nicht nur meine Begeisterung darüber teilen, sondern vor allem erklären will, wie man ECMAScript 6 am besten verwenden kann. Ich hoffe, Sie haben mindestens soviel Spaß beim Lesen, wie ich beim Schreiben hatte.

ecmascript01-teaser_DE

Zu meinem Hintergrund: Ich arbeite bei Microsoft an der Browser Rendering Engine für das Microsoft Edge, welche gegenüber der bisherigen Internet Explorer Engine, die wir im Lauf der Jahre ausgiebig kennen (und lieben?) gelernt haben, ein gewaltiger Schritt nach vorn ist. Mein persönliches Lieblings-Feature dabei ist, dass sie jede Menge ECMAScript 6 unterstützt. Das erleichtert das Schreiben von komplexen Web-Applikationen ungemein.

Laut http://kangax.github.io/compat-table/es6/ und ES6 on dev.modern.ie/platform/status haben wir derzeit fast 70% der ECMAScript 6 Features in Microsoft Edge.

Keine Frage, ich mag es mit JavaScript zu arbeiten, aber wenn ich an großen Projekten wie Babylon.js sitze, greife ich lieber auf TypeScript zurück, das jetzt auch Angular 2 antreibt. Der Grund ist, dass ich bei JavaScript (auch bekannt als ECMAScript 5) einige Syntax-Funktionen vermisse, die andere Programmiersprachen, mit denen ich große Projekte schreibe, mitbringen. Zum Beispiel fehlen mir Klassen und Vererbungen.

Wie Sie von JavaScript auf TypeScript umsteigen, erfahren Sie in diesem Artikel. Eine ausführliche Einführung in TypeScript erhalten Sie in diesem kostenlosen Online-Kurs der Microsoft Virtual Academy (MVA). Den kostenlosen Download zu TypeScript 1.4 für Visual Studio 2013 finden Sie hier.

Eine Klasse erstellen

JavaScript ist eine prototypen-basierte Sprache und mit ECMAScript 5 kann man Klassen und Vererbungen simulieren. Sie möchten die Performance Ihrer Webanwendung verbessern? Hier finden Sie praktische Tipps zur Verbesserung Ihres HTML/JavaScript Codes.

Kennst du unser E-Book-Bundle? Spare jetzt 6,99 €!

E-Book Bundle von Andreas Hecht

Die flexiblen Funktionen von JavaScript erlauben uns, eine Datenkapselung zu simulieren, als ob wir mit Klassen arbeiten würden. Um dies zu erreichen, erweitern wir den Prototyp eines Objekts:

var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    // Methods
    Animal.prototype.doSomething = function () {
        console.log("I'm a " + this.name);
    };
    return Animal;
})();


var lion = new Animal("Lion");
lion.doSomething();

Deutlich wird hier, dass wir eine “Klasse” mit “Eigenschaften” und “Methoden” definiert haben.

Der Konstruktor ist dabei durch die Funktion selbst definiert (function Animal). Dies ermöglicht uns die Instanziierung von Eigenschaften. Durch die Nutzung des Prototyps können wir Funktionen definieren, die wie Instanzmethoden behandelt werden.

Man kann es also durchaus so machen, muss aber Einiges von prototypischer Vererbung verstehen. Und wer an klassenbasierte Sprachen gewöhnt ist, findet das sicher alles etwas verwirrend. Etwas seltsam ist natürlich auch, dass in JavaScript zwar das Schlüsselwort Klasse existiert, es aber nichts bewirkt. ECMAScript 6 hingegen bringt dieses jetzt zum Laufen, und das Ganze auch noch mit einem einfacheren Code:

class AnimalES6 {
    constructor(name) {
        this.name = name;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();

Am Ende haben wir das gleiche Ergebnis. Aber für Entwickler, die gewohnt sind, mit Klassen zu arbeiten, ist das Ganze weitaus leichter zu schreiben und zu lesen. Ein Prototyp ist dazu hier nicht nötig und und man nutzt einfach das Keyword “Constructor” um den Konstruktor zu definieren.

Darüber hinaus bringen Klassen viel neue Semantik mit, die in ECMAScript 5 nicht vorhanden war. So kann ein Konstruktor nicht ohne das Keyword “new” aufgerufen oder Methoden mit “new” konstruiert werden. Eine weitere Veränderung: Methoden sind nicht aufzählbar.

Interessant ist jedoch: Beide Versionen können parallel genutzt werden.

Denn trotz der neuen Keywords, am Ende des Tages landen wir bei einer Funktion mit einem Prototyp, dem eine Funktion hinzugefügt wurde. Eine “Methode” bezeichnet hier einfach eine Funktionseigenschaft des Objekts.

Eine weitere Kernfunktion klassenbasierter Entwicklung, "getter and setter", wird ebenfalls von ES6 unterstützt. Dadurch wird schnell deutlich, was der Sinn von Methoden ist und was sie tun sollen:

class AnimalES6 {
    constructor(name) {
        this.name = name;
        this._age = 0;
    }

    get age() {
        return this._age;
    }

    set age(value) {
        if (value < 0) {
            console.log("We do not support undead animals");
        }

        this._age = value;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();
lionES6.age = 5;

Ganz praktisch, oder?

Gleichzeitig kommt hier einer der typischen Vorbehalte gegenüber JavaScript zum Vorschein: der gar nicht so vertrauliche “private Member” (_age). Mehr zu diesem Thema schrieb ich in diesem Artikel.

Mit einer neuen Funktion von ECMAScript 6 haben wir nun einen eleganteren Weg, dies umzusetzen und zwar mithilfe von “Symbolen”:

var ageSymbol = Symbol();

class AnimalES6 {
    constructor(name) {
        this.name = name;
        this[ageSymbol] = 0;
    }

    get age() {
        return this[ageSymbol];
    }

    set age(value) {
        if (value < 0) {
            console.log("We do not support undead animals");
        }

        this[ageSymbol] = value;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();
lionES6.age = 5;

Was also ist ein Symbol in diesem Zusammenhang? Es ist ein einzigartiger, unveränderlicher Datentyp, der es ermöglicht, eindeutige Objekteigenschaften zu definieren. Ohne das Symbol besteht kein Zugang zu den Eigenschaften.

Die Folge ist ein “persönlicherer” Zugriff auf die Member. Oder zumindest ist dieser Zugriff jetzt nicht mehr so einfach. Symbole helfen zwar bei der Einzigartigkeit von Namen, aber diese Einzigartigkeit allein garantiert noch keinen Datenschutz. Es bedeutet nur: Wenn ein Schlüssel nötig ist, der mit einem anderen nicht kollidieren darf, erzeugen Sie ein neues Symbol.

Dass auch hier noch niemand so ganz "privat" ist, liegt an Object.getOwnPropertySymbols. Damit können andere auf die Symboleigenschaften zugreifen.

Mit Vererbungen arbeiten

Sobald wir Klassen haben, erwarten wir auch Möglichkeiten der Vererbung. Auch hier war es bisher schon möglich, Vererbung in ES5 zu simulieren, aber es war schon eine recht komplexe Angelegenheit.

So sah es zum Beispiel aus, wenn TypeScript Vererbung nachbildete:

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var SwitchBooleanAction = (function (_super) {
     __extends(SwitchBooleanAction, _super);
     function SwitchBooleanAction(triggerOptions, target, propertyPath, condition) {
        _super.call(this, triggerOptions, condition);
        this.propertyPath = propertyPath;
        this._target = target;
     }
     SwitchBooleanAction.prototype.execute = function () {
        this._target[this._property] = !this._target[this._property];
     };
     return SwitchBooleanAction;
})(BABYLON.Action);

Nicht unbedingt sehr leicht zu lesen, oder? Die Alternative von ECMAScript 6 sieht da schon besser aus:

var legsCountSymbol = Symbol();
class InsectES6 extends AnimalES6 {
    constructor(name) {
        super(name);
        this[legsCountSymbol] = 0;
    }

    get legsCount() {
        return this[legsCountSymbol];
    }

    set legsCount(value) {
        if (value < 0) {
            console.log("We do not support nether or interstellar insects");
        }

        this[legsCountSymbol] = value;
    }

    doSomething() {
        super.doSomething();
        console.log("And I have " + this[legsCountSymbol] + " legs!");
    }
}

var spiderES6 = new InsectES6("Spider");
spiderES6.legsCount = 8;
spiderES6.doSomething();

Mithilfe des Keywords “extends” lassen sich Klassen von einer anderen ableiten, also spezialisierte Klassen erzeugen (“child”). Das Keyword “super” stellt dann die Beziehung zur Basisklasse her.

Dank all dieser neuen Features können wir nun Klassen erzeugen und mit Vererbungen arbeiten, ohne uns Hilfe in Hogwarts holen zu müssen, um das Gleiche mit Prototypen zu erreichen.

Warum TypeScript noch relevanter ist als zuvor…

Nachdem wir nun die beschriebenen neuen Funktionen in unserem Browser haben, ist es meiner Meinung nach sogar noch sinnvoller als vorher, TypeScript zu nutzen, um JavaScript-Code zu erstellen.

Schließlich unterstützt TypeScript (1.4) jetzt ECMAScript 6 Code (mit let und const Keywords). Bereits existierender TypeScript-Code kann also weiter verwendet werden, man muss nur noch diese neue Option aktivieren, um ECMAScript 6 Code zu erstellen.

Und wer sich ein paar Zeilen TypeScript genauer anschaut, wird feststellen, dass sie aussehen wie ECMAScript 6 ohne die Typen. Wer heute TypeScript beherrscht, wird morgen also auch ECMAScript 6 schneller verstehen.

Daniel Meixner und Chris Heilmann sprechen in dieser Ausgabe des TechTalk ausführlich über die Neuerungen bei Edge, ECMAScript 2015 und TypeScript.

Fazit

Wer mit TypeScript arbeitet, kann die Funktionen in allen Browsern nutzen, der Code wird in ECMAScript 5 umgewandelt. Wer ECMAScript 6 direkt im Browser nutzen will, muss auf Windows 10 aktualisieren und dort die Rendering Engine von Microsoft Edge testen. Wem das jedoch zuviel Aufwand ist, weil er nur mal eben die neuen Browser-Funktionen ansehen will: remote.modern.ie bietet den Zugriff auf einen Windows 10 Computer mit Microsoft Edge. Das klappt auch mit MacOS oder Linux-Maschinen.

Klar, Microsoft Edge ist nicht der einzige Browser, der den offenen Standard ES6 unterstützt. Wie weit der Support anderer Browser inzwischen reicht, ist hier verzeichnet: http://kangax.github.io/compat-table/es6/

Und um noch einmal euphorisch zu werden: Ich denke die Zukunftsaussichten für JavaScript mit ECMAScript 6 sind wirklich glänzend und ich kann es kaum erwarten, dass es von möglichst vielen Browsern unterstützt wird.

Dieser Artikel ist Teil der Web Dev Tech Series von Microsoft. Wir freuen uns, Microsoft Edge und seine neue EdgeHTML Rendering Engine mit Ihnen zu teilen. Kostenlose Virtual Machines oder Remote Testings für Mac, iOS, Android oder Windows gibt es hier: dev.modern.IE.

David Catuhe

David Catuhe ist Principal Program Manager bei Microsoft mit einem Fokus auf Web-Entwicklung. Er ist der Autor des babylon.js Frameworks, mit dem man 3D Spiele mit HTML5 and WebGL erstellen kann.

Lesen Sie seinen Blog auf MSDN oder folgen Sie ihm auf Twitter: @deltakosh

Hinterlasse einen Kommentar

4 Kommentare auf "ECMAScript 6 besser verstehen: Klassen und Vererbung"

Benachrichtige mich zu:
avatar
Sortiert nach:   neueste | älteste | beste Bewertung
trackback

[…] ersten Teil habe ich euch in das Thema Klassen und Vererbung eingeführt. In diesem Artikel konzentriere ich mich auf Template Strings, ausgehend von meinen […]

ati
Gast

Microsoft, Internet Explorer, Liebe? Man muss doch den interessierten Leser nicht gleich im ersten Absatz vergraulen …

Dieter Petereit
Dr. Web

Hehe. Wir sind halt knallhart 😉

Holger
Gast

Bin nach dem Satz Liebe und IE auch von der Seite gesprungen, habe dem Artikel trotz dem fehlenden Selbstverständnis noch eine Chance gegeben und es nicht bereut. Danke dafür! Und den Bugxplorer setzmer aber trotzdem nicht ein in der Bertelsmann Gruppe.