6215 Stimmen

Was ist der Unterschied zwischen der Verwendung von "let" und "var"?

ECMAScript 6 eingeführt die let Anweisung .

Ich habe gehört, dass man es als eine local Variable, aber ich bin mir immer noch nicht ganz sicher, warum sie sich anders verhält als die var Stichwort.

Was sind die Unterschiede? Wann sollte let verwendet werden anstelle von var ?

133 Stimmen

ECMAScript ist der Standard und let ist enthalten in der Entwurf der 6. Auflage und wird höchstwahrscheinlich in der endgültigen Spezifikation enthalten sein.

12 Stimmen

Véase kangax.github.io/es5-compat-table/es6 für eine aktuelle Unterstützungsmatrix der ES6-Funktionen (einschließlich let). Zum Zeitpunkt der Erstellung dieses Artikels unterstützen Firefox, Chrome und IE11 diese Funktion (obwohl ich glaube, dass die Implementierung von FF nicht ganz Standard ist).

37 Stimmen

Die längste Zeit wusste ich nicht, dass Variablen in einer for-Schleife auf die Funktion, in die sie eingeschlossen war, skaliert wurden. Ich erinnere mich, dass ich dies zum ersten Mal herausfand und dachte, es sei sehr dumm. Jetzt weiß ich allerdings, wie die beiden aus unterschiedlichen Gründen verwendet werden können und dass man in manchen Fällen eine Variable in einer for-Schleife verwenden möchte, ohne dass sie auf den Block skaliert ist.

34voto

zangw Punkte 36978
  • Variable nicht hebend

    let wird nicht heben für den gesamten Bereich des Blocks, in dem sie erscheinen. Im Gegensatz dazu, var könnte wie unten angezogen werden.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }

    Eigentlich, Per @Bergi, Beide var y let werden hochgezogen .

  • Müllabfuhr

    Blockumfang von let nützlich ist, bezieht sich auf Closures und Garbage Collection, um Speicher zurückzugewinnen. Bedenken Sie,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });

    El click Handler-Callback benötigt nicht die hugeData Variable überhaupt nicht. Theoretisch kann nach process(..) läuft, die riesige Datenstruktur hugeData gesammelt werden können. Es ist jedoch möglich, dass eine JS-Engine diese riesige Struktur noch behalten muss, da die click Funktion hat einen Abschluss über den gesamten Bereich.

    Allerdings kann der Blockbereich diese riesige Datenstruktur zu Garbage Collect machen.

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
  • let Schleifen

    let in der Schleife kann bindet es neu zu jeder Iteration der Schleife, wobei darauf zu achten ist, dass ihm der Wert vom Ende der vorherigen Schleifeniteration zugewiesen wird. Überlegen Sie,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }

    Ersetzen Sie jedoch var mit let

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }

    Denn let eine neue lexikalische Umgebung mit diesen Namen für a) den Initialisierungsausdruck b) jede Iteration (vor der Auswertung des Inkrementausdrucks) zu erstellen, weitere Einzelheiten sind aquí .

6 Stimmen

Jep, sie sind hochgezogen, verhalten sich aber so, als ob sie nicht hochgezogen wären, wegen der (Trommelwirbel) Temporal Dead Zone - ein sehr dramatischer Name für einen Identifikator, auf den man nicht zugreifen kann, bis er deklariert wird:-)

0 Stimmen

Let ist also hochgezogen, aber nicht verfügbar? Was ist der Unterschied zu "nicht hochgezogen"?

0 Stimmen

Hoffentlich kommen Brian oder Bergi zurück, um dies zu beantworten. Wird die Vermietungserklärung gehoben, aber nicht der Auftrag? Danke!

34voto

mormegil Punkte 1732

Der Unterschied liegt in der Umfang der jeweils mit deklarierten Variablen.

In der Praxis ergeben sich aus dem unterschiedlichen Anwendungsbereich eine Reihe nützlicher Konsequenzen:

  1. let Variablen sind nur in ihren nächste Umgebung Block ( { ... } ).
  2. let Variablen sind nur in Codezeilen verwendbar, in denen nach die Variable deklariert wird (auch wenn sie werden hochgezogen !).
  3. let Variablen dürfen nicht durch eine nachfolgende Variable neu deklariert werden. var o let .
  4. Global let Variablen werden nicht zu den globalen window Objekt.
  5. let Variablen sind einfach zu benutzen mit Verschlüssen (sie verursachen keine Rennbedingungen ).

Die Einschränkungen, die durch let reduzieren die Sichtbarkeit der Variablen und erhöhen die Wahrscheinlichkeit, dass unerwartete Namenskollisionen frühzeitig erkannt werden. Dies macht es einfacher, die Variablen zu verfolgen und über sie nachzudenken, einschließlich ihrer Erreichbarkeit (hilft bei der Rückgewinnung von ungenutztem Speicher).

Folglich, let Variablen sind weniger problematisch, wenn sie in großen Programmen verwendet werden oder wenn unabhängig entwickelte Frameworks auf neue und unerwartete Weise kombiniert werden.

var kann immer noch nützlich sein, wenn Sie sicher sind, dass Sie den Single-Binding-Effekt bei der Verwendung einer Closure in einer Schleife (#5) oder bei der Deklaration von extern sichtbaren globalen Variablen in Ihrem Code (#4) wünschen. Verwendung von var für Ausfuhren verdrängt werden kann, wenn export aus dem Transpilerbereich in die Kernsprache migriert.

Beispiele

1. Keine Verwendung außerhalb des nächstgelegenen umschließenden Blocks: Dieser Codeblock führt zu einem Referenzfehler, da die zweite Verwendung von x außerhalb des Blocks auftritt, in dem sie mit let :

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

Im Gegensatz dazu wird das gleiche Beispiel mit var funktioniert.

2. Keine Verwendung vor der Anmeldung:
Dieser Codeblock löst eine ReferenceError bevor der Code ausgeführt werden kann, weil x verwendet wird, bevor sie deklariert wird:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

Im Gegensatz dazu wird das gleiche Beispiel mit var analysiert und ausgeführt, ohne Ausnahmen zu verursachen.

3. Keine Neuanmeldung: Der folgende Code zeigt, dass eine Variable, die mit let darf später nicht neu deklariert werden:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4. Globale, die nicht mit window :

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5. Einfache Verwendung mit Verschlüssen: Variablen, die mit var funktionieren nicht gut mit Verschlüssen in Schlaufen. Hier ist eine einfache Schleife, die die Folge von Werten ausgibt, die die Variable i zu verschiedenen Zeitpunkten hat:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

Konkret bedeutet dies:

i is 0
i is 1
i is 2
i is 3
i is 4

In JavaScript verwenden wir Variablen oft zu einem deutlich späteren Zeitpunkt als zum Zeitpunkt ihrer Erstellung. Wenn wir dies demonstrieren, indem wir die Ausgabe mit einer Schließung verzögern, die an setTimeout :

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... die Ausgabe bleibt unverändert, solange wir mit let . Hätten wir im Gegensatz dazu var i stattdessen:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... gibt die Schleife unerwartet fünfmal "i ist 5" aus:

i is 5
i is 5
i is 5
i is 5
i is 5

11 Stimmen

5 wird nicht durch eine Race Condition verursacht. Durch die Verwendung von var anstelle von let ist der Code gleichbedeutend mit: var i = 0; while (i < 5) { doSomethingLater(); i++; } i außerhalb der Schließung liegt, und zu dem Zeitpunkt, zu dem doSomethingLater() ausgeführt wird, i wurde bereits 5 Mal inkrementiert, daher lautet die Ausgabe i is 5 fünfmal. Durch die Verwendung von let wird die Variable i befindet sich innerhalb der Schließung, so dass jeder asynchrone Aufruf seine eigene Kopie von i statt der 'globalen', die mit var .

0 Stimmen

@DanielT.: Ich glaube nicht, dass die Transformation, die Variablendefinition aus dem Schleifeninitialisierer herauszuheben, irgendetwas erklärt. Das ist einfach die normale Definition der Semantik von for . Eine genauere, wenn auch kompliziertere Transformation ist die klassische for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log( i ist ${j} ), 125/*ms*/); })(i); } die einen "Funktionsaktivierungssatz" einführt, um jeden Wert von i mit dem Namen von j innerhalb der Funktion.

27voto

abroz Punkte 371

Hier ist ein Beispiel, um das zu ergänzen, was andere bereits geschrieben haben. Angenommen, Sie wollen ein Array von Funktionen erstellen, adderFunctions wobei jede Funktion ein einzelnes Zahlenargument annimmt und die Summe aus dem Argument und dem Index der Funktion in der Matrix zurückgibt. Der Versuch, eine adderFunctions mit einer Schleife unter Verwendung der var Das Schlüsselwort wird nicht so funktionieren, wie man es vielleicht naiverweise erwartet:

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

Der obige Prozess erzeugt nicht die gewünschte Reihe von Funktionen, weil i geht über die Iteration des for Block, in dem jede Funktion erstellt wurde. Stattdessen wird am Ende der Schleife der i in der Schließung jeder Funktion bezieht sich auf i Wertes am Ende der Schleife (1000) für jede anonyme Funktion in adderFunctions . Das ist überhaupt nicht das, was wir wollten: Wir haben jetzt eine Reihe von 1000 verschiedenen Funktionen im Speicher, die sich genau gleich verhalten. Und wenn wir anschließend den Wert von i wirkt sich die Mutation auf alle adderFunctions .

Wir können es jedoch erneut versuchen, indem wir die let Stichwort:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

Dieses Mal, i wird bei jeder Iteration der for Schleife. Jede Funktion behält nun den Wert von i zum Zeitpunkt der Erstellung der Funktion, und adderFunctions verhält sich wie erwartet.

Wenn Sie sich nun vorstellen, dass Sie die beiden Verhaltensweisen mischen, werden Sie wahrscheinlich verstehen, warum es nicht empfehlenswert ist, die neueren let y const mit den älteren var in derselben Schrift. Dies kann zu einem spektakulär verwirrenden Code führen.

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

Lassen Sie es nicht zu, dass Ihnen das passiert. Verwenden Sie einen Linter.

HINWEIS: Dies ist ein Unterrichtsbeispiel, das die var / let Verhalten in Schleifen und mit Funktionsabschlüssen, die auch leicht zu verstehen wären. Dies wäre eine schreckliche Art, Zahlen zu addieren. Aber die allgemeine Technik der Erfassung von Daten in anonymen Funktionsabschlüssen könnte in der realen Welt in anderen Zusammenhängen anzutreffen sein. YMMV.

2 Stimmen

@aborz: Auch sehr coole anonyme Funktionssyntax im zweiten Beispiel. So bin ich es von C# gewohnt. Ich habe heute etwas gelernt.

0 Stimmen

Berichtigung: Technisch gesehen ist die hier beschriebene Syntax der Arrow-Funktion => developer.mozilla.org/de-US/docs/Web/JavaScript/Reference/

3 Stimmen

Eigentlich brauchen Sie keine let value = i; . En for Anweisung erzeugt einen lexikalischen Block.

24voto

Abdennour TOUMI Punkte 75271

Die folgenden zwei Funktionen sollen den Unterschied verdeutlichen:

function varTest() {
    var x = 31;
    if (true) {
        var x = 71;  // Same variable!
        console.log(x);  // 71
    }
    console.log(x);  // 71
}

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // Different variable
        console.log(x);  // 71
    }
    console.log(x);  // 31
}

22voto

Ran Turner Punkte 8004

Diese Erklärung wurde einem Artikel entnommen, den ich unter Mittel :

Hoisting ist ein JavaScript-Mechanismus, bei dem Variablen und Funktionen Deklarationen durch den Parser an den Anfang ihres Geltungsbereichs verschoben werden, der der den Quellcode in eine Zwischendarstellung liest, bevor die bevor der JavaScript-Interpreter mit der eigentlichen Codeausführung beginnt. Es ist also eigentlich egal, wo Variablen oder Funktionen deklariert werden, sie werden an den Anfang ihres Geltungsbereichs verschoben, unabhängig davon, ob ihr Geltungsbereich global oder lokal ist. Dies bedeutet, dass

console.log (hi);     
var hi = "say hi";

wird tatsächlich interpretiert als

var hi = undefined;
console.log (hi);
hi = "say hi";

Wie wir soeben gesehen haben, var Variablen werden an die Spitze gehievt ihres Geltungsbereichs gehoben und mit dem Wert "undefined" initialisiert initialisiert, was bedeutet, dass wir ihren Wert zuweisen können, bevor wir tatsächlich bevor sie im Code deklariert werden, wie hier:

hi = “say hi”
console.log (hi); // say hi
var hi;

Was die Funktionsdeklarationen betrifft, so können wir sie vor der eigentlichen Deklaration wie folgt aufrufen:

sayHi(); // Hi

function sayHi() {
   console.log('Hi');
};

Funktionsausdrücke hingegen werden nicht hochgezogen, so dass der folgende Fehler auftritt:

sayHi(); //Output: "TypeError: sayHi is not a function

var sayHi = function() {
  console.log('Hi');
}; 

Mit ES6 haben JavaScript-Entwickler die let y const Schlüsselwörter. Während let y const sind blockorientiert und nicht funktionsorientiert skaliert als var sollte es keinen Unterschied machen, wenn man über ihre Hebeverhalten. Wir beginnen mit dem Ende, den JavaScript-Zügen let und const .

console.log(hi); // Output: Cannot access 'hi' before initialization 
let hi = 'Hi';

Wie wir oben sehen können, let erlaubt es uns nicht, nicht deklarierte Variablen, daher gibt der Interpreter explizit einen Referenzfehler aus aus, der anzeigt, dass die hi Variable kann nicht vor Initialisierung zugegriffen werden. Der gleiche Fehler tritt auf, wenn wir die obige let zu const

console.log(hi); // Output: Cannot access 'hi' before initialization
const hi = 'Hi';

Unterm Strich sucht der JavaScript-Parser also nach der Variable Deklarationen und Funktionen und hebt sie an den Anfang ihres Bereichs vor der Codeausführung und weist ihnen Werte im Speicher zu, so dass der Interpreter bei der Ausführung des Codes auf sie stößt, erkennt er Code stößt, erkennt er sie und kann den Code mit den ihnen zugewiesenen Werten ausführen kann. Variablen, die mit let o const bleiben zu Beginn der Ausführung uninitialisiert bleiben, während die Variablen deklariert mit var werden mit einem Wert von undefined .

Ich habe diese visuelle Illustration hinzugefügt, um zu verstehen, wie die hochgezogenen Variablen und Funktionen im Speicher gespeichert werden enter image description here

0 Stimmen

Bei Stack Overflow ist eine korrekte Namensnennung für zitierte Inhalte erforderlich. Dazu gehört die explizite Offenlegung der Zugehörigkeit und die klare Anzeige, wenn Inhalte von einem anderen Ort kopiert wurden... selbst wenn Sie der Autor sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X