Anders als im ECMAScript 5 rüstet ES6 nicht nur hier und da ein paar Funktionen nach, sondern bietet für viele Neuerungen neue Syntax und Konzepte, wobei einschränkend gesagt werden muss, dass viele der „Neuheiten“ so neu gar nicht sind. Das Meiste ist irgendwo geklaut: aus proprietären Browser-Erweiterungen, aus CoffeeScript, anderen Programmiersprachen oder gar aus dem gescheiterten ES4-Standard. In diese Kategorie der nicht ganz so neuen Neuheiten fallen auch die neuen blockgebundenen Variablen (let
) und Konstanten (const
), um die es in diesem Artikel geht. Die neuen Features funktionieren zur Zeit in allem, was auf neuere V8-Engines aufsetzt, also Chrome und Node, wobei es nicht mehr lange dauern dürfte, bis auch andere JS-Implementierungen nachrüsten. Wenn es endlich so weit ist, haben die JavaScript-Programmierer dieser Welt zwei Probleme mit Variablen weniger.
Zwei Probleme mit Variablen
Problem Nummer 1 ist, dass in ECMAScript 5 und älter der Geltungsbereich von Variablen die Funktion ist, in der sie deklariert wurden. Wenn man den folgenden Schnipsel ausführt …
function foobar(foo){
if(foo){
var bar = 42;
}
};
… wird der Code interprtiert, als hätte man das folgende geschrieben:
function foobar(foo){
var bar;
if(foo){
bar = 42;
}
};
Dieses Verhalten bringt es mit sich, dass man, wenn man Variablen innerhalb von Blöcken deklariert, damit auch die Umwelt außerhalb der Blöcke verunreinigt:
function foobar(foo){
if(foo){
var bar = 42;
}
// "bar" ist hier auch noch 42
};
Das mag zwar in vielen Fällen wie dem obrigen nicht groß stören, in anderen hingegen schon:
var bar = { 'hallo': 1337 };
function foobar(foo){
if(bar[foo]){
return bar[foo];
}
else {
var bar = 42 * 666;
return bar;
}
};
var x = foobar('hallo'); // Na, wer weiß was hier passiert?
Selbst wenn dieses Verhalten nicht oft zu Problemen führt, so ist es doch weder ist es sonderlich intuitiv, noch wird es wirklich gebraucht. Im Gegenzug ist es recht mühsam, in ECMAScript 5 und älter Variablen auf Blöcke zu beschränken, denn außer sofort ausgeführten Funktionen hilft hier nichts:
function foobar(foo){
whatever();
if(foo){
(function(){
var bar = 42;
})();
}
// "bar" ist hier undefined
};
Das funktioniert zwar, ist aber der Code-Eleganz nicht besonders zuträglich und für alle mit eher mittlguten JavaScript-Kenntnissen unnötig verwirrend.
Das zweite Problem ist, dass es in ECMAScript 5 und älter eben nur Variablen und keine Konstanten gibt. Es existieren zwar Konventionen, gemäß derer man Variablen durch Allcaps-Namen als „Konstanten“ kennzeichnet, doch das allein hält im Zweifelsfall niemanden davon ab, die „Konstante“ zu überschreiben:
var KONSTANTE = 42;
KONSTANTE = 1337; // Funktioniert!
Die einzige in ES5 denkbare Lösung besteht darin, mittels Property Descriptor die gewünschte Konstante als nicht-überschreibbare Eigenschaft auf einem Objekt anzulegen:
Object.defineProperty(window, 'KONSTANTE', {
writable: false,
enumerable: true,
configurable: false,
value: 42
});
KONSTANTE = 1337; // TypeError: Cannot assign to read only property 'KONSTANTE'
Auch hier gilt: das funktioniert zwar, aber als ersthafte Lösung kann man dieses Konvolut wohl niemandem verkaufen. ECMAScript 6 schafft nun die Probleme rund um Konstranten und var
aus der Welt, indem mit let
und const
zwei Alternativ-Formen von Variablen eingeführt werden.
Lösung 1: let
statt var
Verwendet man zur Deklaration einer neuen Variable das Schlüsselwort let
statt var
, so erhält man eine an den Block gebundene Variable, die jenseits ihrer Schleife oder Kontrollstruktur nicht sichtbar ist. Damit bleiben zum Beispiel die Zähler einer For-Schleife ausschließlich innerhalb der Schleife gültig:
for(let i = 0; i < 4; i++){
}
console.log(i); // ReferenceError: i is not defined
Hierfür braucht es nicht zwingend echte Kontrollstrukturen oder Schleifen, auch bei leeren Blöcken funktioniert let
:
{
let i = 42;
}
console.log(i); // ReferenceError: i is not defined
Aus der Blockbindung folgt, dass let
-Deklarationen nur in einem Block, in Funktionscode oder auf Programmebene vorkommen dürfen:
if(true) var i = 42; // Klappt
if(true) let i = 42; // SyntaxError: Illegal let declaration in unprotected statement context
if(true){ let i = 42; } // Klappt
Ansonsten verhält sich let
ziemlich genau wie var
und unterliegt auch den gleichen Einschränkungen – auch let window = 42
kann in einem Browser das Window-Objekt nicht überschreiben. Insgesamt ist let
also nichts weiter als ein repariertes var
, dass im JavaScript-Code der Zukunft ersteres in den meisten Fällen anstelle von var
ersetzt werden dürfte.
Lösung 2: const
statt var
Mit const
lassen sich echte Konstanten anlegen, die auch wirklich nicht überschrieben werden können:
const KONSTANTE = 42;
KONSTANTE = 1337; // TypeError: Cannot assign to read only property 'KONSTANTE'
Genau wie let
-Variablen sind auch const
-Konstanten an ihren Block gebunden:
if(true){
const KONSTANTE = 42;
}
console.log(KONSTANTE); // ReferenceError: KONSTANTE is not defined
Anders als Variablen (egal ob let
oder var
) müssen Konstanzen logischerweise auch immer mit einem Wert initialisiert werden:
var foo; // Kein Problem
let bar; // Kein Problem
const BAZ; // SyntaxError: Unexpected token ;
Auch wenn JavaScript nun echte Konstanten kennt, ist es sinnvoll, auch bei deren Verwendung weiterhin den Bezeichner komplett groß zu schreiben – so ist immer klar, wann man es mit einer Variablen oder einer Konstanten zu tun hat.
Der Status
Über let
und const
besteht in der ES6-Arbeitsgruppe weitgehend Konsens und die noch offenen Fragen (nachzulesen auf den entsprechenden Wiki-Seiten) betreffen allesamt Sonderfälle. Aufhalten lassen werden sich diese Features nicht mehr. Und so zeigt die allmächtige Kompatibilitätstabelle schon recht viel grün, doch echte Unterstützung für let
und const
findet sich zur Zeit nur in den V8-Engines von Chrome ab Version 18 und Node.js. Ab Firefox Version 2.0 sowie einigen anderen Browsern existieren auch Implementierungen von let
und const
, die jedoch nicht mit dem ES6-Standard kompatibel sind und die man daher weiträumig umfahren sollte.