letconstlet und constconst erstellt unveränderliche Variablenconst macht den Wert nicht unveränderlichconst in Schleifenkörpernvar-deklarierten Variablenlet-deklarierten Variablentypeof wirft einen ReferenceError für eine Variable in der TDZlet und const in Schleifenkopfzeilenfor-Schleifefor-of-Schleife und for-in-Schleifeconst versus let versus varES6 bietet zwei neue Möglichkeiten zur Deklaration von Variablen: let und const, die die ES5-Methode zur Variablendeklaration, var, größtenteils ersetzen.
let let funktioniert ähnlich wie var, aber die Variable, die es deklariert, ist *block-bezogen*, sie existiert nur innerhalb des aktuellen Blocks. var ist *funktionsbezogen*.
Im folgenden Code sehen Sie, dass die mit let deklarierte Variable tmp nur innerhalb des Blocks existiert, der in Zeile A beginnt
function order(x, y) {
if (x > y) { // (A)
let tmp = x;
x = y;
y = tmp;
}
console.log(tmp===x); // ReferenceError: tmp is not defined
return [x, y];
}
const const funktioniert wie let, aber die Variable, die Sie deklarieren, muss sofort initialisiert werden, mit einem Wert, der danach nicht mehr geändert werden kann.
const foo;
// SyntaxError: missing = in const declaration
const bar = 123;
bar = 456;
// TypeError: `bar` is read-only
Da for-of pro Schleifeniteration eine *Bindung* (Speicherplatz für eine Variable) erstellt, ist es in Ordnung, die Schleifenvariable mit const zu deklarieren
for (const x of ['a', 'b']) {
console.log(x);
}
// Output:
// a
// b
Die folgende Tabelle gibt einen Überblick über sechs Möglichkeiten, wie Variablen in ES6 deklariert werden können (inspiriert von einer Tabelle von kangax)
| Hoisting | Geltungsbereich | Erzeugt globale Eigenschaften | |
|---|---|---|---|
var |
Deklaration | Funktion | Ja |
let |
Temporäre Sackgasse | Block | Nein |
const |
Temporäre Sackgasse | Block | Nein |
Funktion |
Vollständig | Block | Ja |
class |
Nein | Block | Nein |
import |
Vollständig | Modul-global | Nein |
let und const Sowohl let als auch const erstellen Variablen, die *block-bezogen* sind – sie existieren nur innerhalb des innersten Blocks, der sie umschließt. Der folgende Code zeigt, dass die mit const deklarierte Variable tmp nur innerhalb des Blocks der if-Anweisung existiert
function func() {
if (true) {
const tmp = 123;
}
console.log(tmp); // ReferenceError: tmp is not defined
}
Im Gegensatz dazu sind var-deklarierte Variablen funktionsbezogen
function func() {
if (true) {
var tmp = 123;
}
console.log(tmp); // 123
}
Block-Scoping bedeutet, dass Sie Variablen innerhalb einer Funktion überschatten können
function func() {
const foo = 5;
if (···) {
const foo = 10; // shadows outer `foo`
console.log(foo); // 10
}
console.log(foo); // 5
}
const erstellt unveränderliche Variablen Variablen, die mit let erstellt wurden, sind veränderlich
let foo = 'abc';
foo = 'def';
console.log(foo); // def
Konstanten, Variablen, die mit const erstellt wurden, sind unveränderlich – Sie können ihnen keine anderen Werte zuweisen
const foo = 'abc';
foo = 'def'; // TypeError
const macht den Wert nicht unveränderlich const bedeutet nur, dass eine Variable immer denselben Wert hat, aber es bedeutet nicht, dass der Wert selbst unveränderlich ist oder wird. Zum Beispiel ist obj eine Konstante, aber der Wert, auf den sie zeigt, ist veränderlich – wir können ihr eine Eigenschaft hinzufügen
const obj = {};
obj.prop = 123;
console.log(obj.prop); // 123
Wir können obj jedoch keinen anderen Wert zuweisen
obj = {}; // TypeError
Wenn Sie möchten, dass der Wert von obj unveränderlich ist, müssen Sie sich selbst darum kümmern. Zum Beispiel, indem Sie ihn einfrieren
const obj = Object.freeze({});
obj.prop = 123; // TypeError
Object.freeze() ist flach Beachten Sie, dass Object.freeze() *flach* ist, es friert nur die Eigenschaften seines Arguments ein, nicht die Objekte, die in seinen Eigenschaften gespeichert sind. Zum Beispiel ist das Objekt obj eingefroren
> const obj = Object.freeze({ foo: {} });
> obj.bar = 123
TypeError: Can't add property bar, object is not extensible
> obj.foo = {}
TypeError: Cannot assign to read only property 'foo' of #<Object>
Aber das Objekt obj.foo nicht.
> obj.foo.qux = 'abc';
> obj.foo.qux
'abc'
const in Schleifenkörpern Sobald eine const-Variable erstellt wurde, kann sie nicht mehr geändert werden. Das bedeutet aber nicht, dass Sie ihren Geltungsbereich nicht wieder betreten und neu starten können, mit einem neuen Wert. Zum Beispiel über eine Schleife
function logArgs(...args) {
for (const [index, elem] of args.entries()) { // (A)
const message = index + '. ' + elem; // (B)
console.log(message);
}
}
logArgs('Hello', 'everyone');
// Output:
// 0. Hello
// 1. everyone
In diesem Code gibt es zwei const-Deklarationen, in Zeile A und in Zeile B. Und während jeder Schleifeniteration haben ihre Konstanten unterschiedliche Werte.
Eine Variable, die mit let oder const deklariert wurde, hat eine sogenannte *temporäre Sackgasse* (TDZ): Wenn ihr Geltungsbereich betreten wird, kann sie nicht aufgerufen werden (lesen oder schreiben), bis die Ausführung die Deklaration erreicht. Vergleichen wir die Lebenszyklen von var-deklarierten Variablen (die keine TDZs haben) und let-deklarierten Variablen (die TDZs haben).
var-deklarierten Variablen var-Variablen haben keine temporären Sackgassen. Ihr Lebenszyklus umfasst die folgenden Schritte
var-Variable wird Speicherplatz (eine *Bindung*) für sie erstellt. Die Variable wird sofort mit undefined initialisiert.undefined.let-deklarierten Variablen Variablen, die über let deklariert wurden, haben temporäre Sackgassen und ihr Lebenszyklus sieht wie folgt aus
let-Variable wird Speicherplatz (eine *Bindung*) für sie erstellt. Die Variable bleibt uninitialisiert.ReferenceError aus.undefined gesetzt.const-Variablen funktionieren ähnlich wie let-Variablen, müssen aber einen Initialisierer haben (d.h. sofort einen Wert zugewiesen bekommen) und können nicht geändert werden.
Innerhalb einer TDZ wird eine Ausnahme ausgelöst, wenn eine Variable gelesen oder geschrieben wird
let tmp = true;
if (true) { // enter new scope, TDZ starts
// Uninitialized binding for `tmp` is created
console.log(tmp); // ReferenceError
let tmp; // TDZ ends, `tmp` is initialized with `undefined`
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
console.log(tmp); // true
Wenn ein Initialisierer vorhanden ist, endet die TDZ *nachdem* der Initialisierer ausgewertet und das Ergebnis der Variable zugewiesen wurde
let foo = console.log(foo); // ReferenceError
Der folgende Code zeigt, dass die Sackgasse wirklich *temporär* (zeitlich bedingt) und nicht räumlich (örtlich bedingt) ist
if (true) { // enter new scope, TDZ starts
const func = function () {
console.log(myVar); // OK!
};
// Here we are within the TDZ and
// accessing `myVar` would cause a `ReferenceError`
let myVar = 3; // TDZ ends
func(); // called outside TDZ
}
typeof wirft einen ReferenceError für eine Variable in der TDZ Wenn Sie über typeof auf eine Variable in der temporären Sackgasse zugreifen, erhalten Sie eine Ausnahme
if (true) {
console.log(typeof foo); // ReferenceError (TDZ)
console.log(typeof aVariableThatDoesntExist); // 'undefined'
let foo;
}
Warum? Die Begründung lautet wie folgt: foo ist nicht unde deklariert, sondern uninitialisiert. Sie sollten sich seiner Existenz bewusst sein, sind es aber nicht. Daher ist es wünschenswert, gewarnt zu werden.
Darüber hinaus ist diese Art der Überprüfung nur nützlich, um globale Variablen bedingt zu erstellen. Das ist etwas, das Sie in normalen Programmen nicht tun müssen.
Wenn es um das bedingte Erstellen von Variablen geht, haben Sie zwei Möglichkeiten.
Option 1 – typeof und var
if (typeof someGlobal === 'undefined') {
var someGlobal = { ··· };
}
Diese Option funktioniert nur im globalen Geltungsbereich (und daher nicht innerhalb von ES6-Modulen).
Option 2 – window
if (!('someGlobal' in window)) {
window.someGlobal = { ··· };
}
Es gibt mehrere Gründe, warum const und let temporäre Sackgassen haben
const: Die korrekte Funktionsweise von const ist schwierig. Zitiert von Allen Wirfs-Brock: „TDZs ... bieten eine rationale Semantik für const. Es gab erhebliche technische Diskussionen zu diesem Thema und TDZs erwiesen sich als die beste Lösung.“ let hat ebenfalls eine temporäre Sackgasse, damit der Wechsel zwischen let und const das Verhalten nicht unerwartet verändert.undefined ist, kann dieser Wert im Konflikt mit der Garantie stehen, die ihr Guard gibt.Quellen dieses Abschnitts
let und const in Schleifenkopfzeilen Die folgenden Schleifen erlauben Ihnen, Variablen in ihren Kopfzeilen zu deklarieren
forfor-infor-ofUm eine Deklaration vorzunehmen, können Sie entweder var, let oder const verwenden. Jede davon hat eine andere Wirkung, wie ich als Nächstes erklären werde.
for-Schleife Das Deklarieren einer Variable mit var im Kopf einer for-Schleife erstellt eine einzelne *Bindung* (Speicherplatz) für diese Variable
const arr = [];
for (var i=0; i < 3; i++) {
arr.push(() => i);
}
arr.map(x => x()); // [3,3,3]
Jedes i in den Körpern der drei Pfeilfunktionen bezieht sich auf dieselbe Bindung, weshalb sie alle denselben Wert zurückgeben.
Wenn Sie eine Variable mit let deklarieren, wird für jede Schleifeniteration eine neue Bindung erstellt
const arr = [];
for (let i=0; i < 3; i++) {
arr.push(() => i);
}
arr.map(x => x()); // [0,1,2]
Diesmal bezieht sich jedes i auf die Bindung einer bestimmten Iteration und bewahrt den zu diesem Zeitpunkt aktuellen Wert. Daher gibt jede Pfeilfunktion einen anderen Wert zurück.
const funktioniert wie var, aber Sie können den Anfangswert einer mit const deklarierten Variable nicht ändern
// TypeError: Assignment to constant variable
// (due to i++)
for (const i=0; i<3; i++) {
console.log(i);
}
Das Erhalten einer neuen Bindung für jede Iteration mag zunächst seltsam erscheinen, ist aber sehr nützlich, wenn Sie Schleifen verwenden, um Funktionen zu erstellen, die sich auf Schleifenvariablen beziehen, wie in einem späteren Abschnitt erklärt.
for-of-Schleife und for-in-Schleife In einer for-of-Schleife erstellt var eine einzelne Bindung
const arr = [];
for (var i of [0, 1, 2]) {
arr.push(() => i);
}
arr.map(x => x()); // [2,2,2]
const erstellt eine unveränderliche Bindung pro Iteration
const arr = [];
for (const i of [0, 1, 2]) {
arr.push(() => i);
}
arr.map(x => x()); // [0,1,2]
let erstellt ebenfalls eine Bindung pro Iteration, aber die von ihm erstellten Bindungen sind veränderlich.
Die for-in-Schleife funktioniert ähnlich wie die for-of-Schleife.
Das Folgende ist eine HTML-Seite, die drei Links anzeigt
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="content"></div>
<script>
const entries = [
['yes', 'ja'],
['no', 'nein'],
['perhaps', 'vielleicht'],
];
const content = document.getElementById('content');
for (const [source, target] of entries) { // (A)
content.insertAdjacentHTML('beforeend',
`<div><a id="${source}" href="">${source}</a></div>`);
document.getElementById(source).addEventListener(
'click', (event) => {
event.preventDefault();
alert(target); // (B)
});
}
</script>
</body>
</html>
Was angezeigt wird, hängt von der Variable target (Zeile B) ab. Wenn wir statt const in Zeile A var verwendet hätten, gäbe es eine einzige Bindung für die gesamte Schleife und target hätte danach den Wert 'vielleicht'. Daher würden Sie, egal auf welchen Link Sie klicken, immer die Übersetzung 'vielleicht' erhalten.
Glücklicherweise erhalten wir mit const eine Bindung pro Schleifeniteration und die Übersetzungen werden korrekt angezeigt.
Wenn Sie eine Variable mit let deklarieren, die denselben Namen wie ein Parameter hat, erhalten Sie einen statischen Fehler (zur Ladezeit)
function func(arg) {
let arg; // static error: duplicate declaration of `arg`
}
Das Gleiche innerhalb eines Blocks überschattet den Parameter
function func(arg) {
{
let arg; // shadows parameter `arg`
}
}
Im Gegensatz dazu bewirkt das Deklarieren einer Variable mit var, die denselben Namen wie ein Parameter hat, nichts, genau wie das erneute Deklarieren einer var-Variable im selben Geltungsbereich nichts bewirkt.
function func(arg) {
var arg; // does nothing
}
function func(arg) {
{
// We are still in same `var` scope as `arg`
var arg; // does nothing
}
}
Wenn Parameter Standardwerte haben, werden sie wie eine Sequenz von let-Anweisungen behandelt und unterliegen temporären Sackgassen
// OK: `y` accesses `x` after it has been declared
function foo(x=1, y=x) {
return [x, y];
}
foo(); // [1,1]
// Exception: `x` tries to access `y` within TDZ
function bar(x=y, y=2) {
return [x, y];
}
bar(); // ReferenceError
Der Geltungsbereich von Parameter-Standardwerten ist getrennt vom Geltungsbereich des Körpers (der erstere umschließt letzteren). Das bedeutet, dass Methoden oder Funktionen, die „innerhalb“ von Parameter-Standardwerten definiert sind, die lokalen Variablen des Körpers nicht sehen
const foo = 'outer';
function bar(func = x => foo) {
const foo = 'inner';
console.log(func()); // outer
}
bar();
Das globale Objekt von JavaScript (window in Webbrowsern, global in Node.js) ist eher ein Fehler als eine Funktion, insbesondere in Bezug auf die Leistung. Deshalb ist es sinnvoll, dass ES6 eine Unterscheidung einführt
var-Deklarationenlet-Deklarationenconst-DeklarationenBeachten Sie, dass die Körper von Modulen nicht im globalen Geltungsbereich ausgeführt werden, sondern nur Skripte. Daher bilden die Umgebungen für verschiedene Variablen die folgende Kette.
Funktionsdeklarationen...
let.var.Der folgende Code zeigt das Hoisting von Funktionsdeklarationen
{ // Enter a new scope
console.log(foo()); // OK, due to hoisting
function foo() {
return 'hello';
}
}
Klassendeklarationen...
Dass Klassen nicht gehoisted werden, mag überraschend sein, denn unter der Haube erstellen sie Funktionen. Die Begründung für dieses Verhalten ist, dass die Werte ihrer extends-Klauseln über Ausdrücke definiert werden und diese Ausdrücke zur richtigen Zeit ausgeführt werden müssen.
{ // Enter a new scope
const identity = x => x;
// Here we are in the temporal dead zone of `MyClass`
const inst = new MyClass(); // ReferenceError
// Note the expression in the `extends` clause
class MyClass extends identity(Object) {
}
}
const versus let versus var Ich empfehle, immer entweder let oder const zu verwenden
const. Sie können es verwenden, wenn eine Variable ihren Wert nie ändert. Mit anderen Worten: Die Variable sollte nie die linke Seite einer Zuweisung oder Operand von ++ oder -- sein. Das Ändern eines Objekts, auf das eine const-Variable verweist, ist erlaubt const foo = {};
foo.prop = 123; // OK
Sie können const sogar in einer for-of-Schleife verwenden, da pro Schleifeniteration eine (unveränderliche) Bindung erstellt wird
for (const x of ['a', 'b']) {
console.log(x);
}
// Output:
// a
// b
Innerhalb des Körpers der for-of-Schleife kann x nicht geändert werden.
let – wenn sich der Anfangswert einer Variable später ändert. let counter = 0; // initial value
counter++; // change
let obj = {}; // initial value
obj = { foo: 123 }; // change
var.Wenn Sie diese Regeln befolgen, wird var nur in Legacy-Code erscheinen, als Signal, dass sorgfältiges Refactoring erforderlich ist.
var tut eine Sache, die let und const nicht tun: Variablen, die damit deklariert werden, werden zu Eigenschaften des globalen Objekts. Das ist im Allgemeinen keine gute Sache. Sie können den gleichen Effekt erzielen, indem Sie window (in Browsern) oder global (in Node.js) zuweisen.
Eine Alternative zu den gerade erwähnten Stilregeln ist, const nur für Dinge zu verwenden, die vollständig unveränderlich sind (Primitive Werte und eingefrorene Objekte). Dann haben wir zwei Ansätze
const: const kennzeichnet unveränderliche Bindungen.let: const kennzeichnet unveränderliche Werte.Ich neige leicht zu #1, aber #2 ist auch in Ordnung.