ECMAScript 2015: const in for-Schleifen

83

Welches der beiden folgenden Codefragmente (oder keines / beide) sollte in einer vollständigen Implementierung von ECMAScript 2015 funktionieren:

for (const e of a)

for (const i = 0; i < a.length; i += 1)

Nach meinem Verständnis sollte das erste Beispiel funktionieren, da ees für jede Iteration initialisiert wird. Sollte dies nicht auch iin der zweiten Version der Fall sein ?

Ich bin verwirrt, weil vorhandene Implementierungen (Babel, IE, Firefox, Chrome, ESLint) nicht konsistent zu sein scheinen und eine vollständige Implementierung constmit verschiedenen Verhaltensweisen der beiden Schleifenvarianten aufweisen. Ich bin auch nicht in der Lage, einen konkreten Punkt im Standard zu finden, daher wäre das sehr dankbar.

adrianp
quelle
1
const ist für Konstanten xx, sollten Sie stattdessen let verwenden
Patsy Issa
3
@ JamesThorpe Nein, meine Frage versucht einfach herauszufinden, wie sich das Verhalten verhalten soll. Beispielsweise betrachtet ESLint das erste Beispiel als OK (und bevorzugt mit prefer-constOption) und das zweite als ungültig. Die meisten Browser-Implementierungen betrachten beide Beispiele als ungültig.
Adrianp
4
AFAIK der erste ist in Ordnung, da er bei jeder Iteration neu initialisiert wird. Es funktioniert in Chrome.
Lyschoening
@lyschoening und das sollte auch für das zweite Beispiel nicht gelten?
Adrianp
4
@adrianp definitiv nicht das zweite Beispiel. Die reguläre for - Schleife ist im Wesentlichen äquivalent zu{const i = 0; while(i < a.length) { /* for body */ i += 1}}
lyschoening

Antworten:

91

Die folgende for-of-Schleife funktioniert:

for (const e of a)

Die ES6-Spezifikation beschreibt dies wie folgt:

ForDeclaration: LetOrConst ForBinding

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-in-and-for-of-statements-static-semantics-boundnames

Der Imperativ für die Schleife funktioniert nicht:

for (const i = 0; i < a.length; i += 1)

Dies liegt daran, dass die Deklaration nur einmal ausgewertet wird, bevor der Schleifenkörper ausgeführt wird.

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-statement-runtime-semantics-labelledevaluation

lyschoening
quelle
3
Warum verlinken Sie auf den Entwurf und nicht auf die endgültige Spezifikation? ecma-international.org/ecma-262/6.0/index.html . Außerdem zitieren Sie die falsche Bewertungsregel für die forSchleife. const ...ist kein Ausdruck. Sie müssen sich die Regel ansehen for ( LexicalDeclaration Expression ; Expression) Statement .
Felix Kling
@FelixKling hatte noch den alten Link mit einem Lesezeichen versehen. Sie haben Recht mit der Bewertungsregel. Die Schlussfolgerung sollte immer noch dieselbe sein, da eine unveränderliche Bindung genau einmal erstellt wird (in Schritt 5).
Lyschoening
4
Richtig, aber ich denke, der Hinweis befindet sich in Schritt 9. consts werden nicht pro Iteration neu deklariert.
Felix Kling
4
for (const e of a)scheint in der neuesten Version von Firefox NICHT zu funktionieren. Ich bekommeSyntaxError: invalid for/in left-hand side
Chris_F
2
In MDN-Dokumenten heißt es außerdem: "Sie können const anstelle von let verwenden, wenn Sie die Variable innerhalb des Blocks nicht neu zuweisen." developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Phil Gibbins
44

Ich werde die Spezifikation diesmal nicht zitieren, weil ich denke, dass es einfacher ist zu verstehen, was anhand eines Beispiels passiert.

for (const e of a) …

Ist im Grunde gleichbedeutend mit

{
    const __it = a[Symbol.iterator]();
    let __res;
    while ((__res = __it.next()) && !__res.done) {
        const e = __res.value;
        …
    }
}

Der Einfachheit halber habe ich ignoriert, dass es eine TDZ mit efür den aAusdruck gibt, und die verschiedenen __it.return()/ __it.throw(e)Aufrufe für den Fall, dass die Schleife vorzeitig ( breakoder throwim Körper) beendet wird.

for (const i = 0; i < a.length; i += 1) …

ist im Grunde gleichbedeutend mit

{
    const i = 0;
    while (i < a.length) {
        …
        i += 1;
    }
}

Im Gegensatz dazulet wird eine constDeklaration in einer forSchleife nicht bei jeder Schleifeniteration neu deklariert (und der Initialisierer wird ohnehin nicht erneut ausgeführt). Wenn Sie nicht breakin der ersten Iteration, werden Sie i +=hier werfen.

Bergi
quelle
Für mich (Knoten 5.0.0) funktioniert die for-of-Schleife nicht wie erwartet. Nach 'sei a = [1,3,5], b = []' und 'für (Konstante von a) {b.push (e)}' erhalte ich 'b == [1, 1, 1]' . Ein Knotenfehler oder beabsichtigtes Verhalten? Gemäß Ihrem Code würde ich pro Iteration eine neue Zuweisung von 'const e = __res.value' erwarten.
Jürgen Strobel
2
@ JürgenStrobel: Ein alter Knotenfehler, der consteinen varähnlichen Umfang hat und keine Zuweisung erfordert. Verwenden Sie den strengen Modus.
Bergi
Ich weiß, dass dies alt ist, aber eine leichte Korrektur : while ( ( __res = __it.next() ) && !__res.done) {. Andernfalls __resendet trueoderfalse
JDB erinnert sich noch an Monica
@ JDB Danke, behoben! Übrigens wäre es mir gut gegangen, wenn Sie es gerade bearbeitet hätten.
Bergi
3

Ihr zweites Beispiel sollte definitiv nicht funktionieren, da ies einmal deklariert wird und nicht bei jeder Iteration. Dies ist nur eine Funktion der Funktionsweise dieser Kategorie von Schleifen.

Sie können dies in einem normalen Browser versuchen:

for (var i = 0, otherVar = ""; i < [1,2,3,4].length; i += 1){
  console.log(otherVar)
  otherVar = "If otherVar was initialized on each iteration, then you would never read me.";
}

Es ist nicht der Fall, der constin forSchleifen völlig unzulässig ist . Nur fordas ändert const ist.

Diese sind gültig:

for(const i = 0;;){ break } 
for(const i = 0; i < 10;){ break; } 

Diese sind ungültig:

for(const i = 0;;){ ++i; break; } 
for(const i = 0;;++i){ if(i > 0) break; }

Ich bin mir nicht sicher, warum Firefox nach dem Lesen der ES2015-Spezifikation einen SyntaxError ausgibt (obwohl ich sicher bin, dass die cleveren Leute bei Mozilla korrekt sind), es scheint, als sollte dies eine Ausnahme auslösen:

Erstellen Sie eine neue, aber nicht initialisierte unveränderliche Bindung in einem Umgebungsdatensatz. Der String-Wert N ist der Text des gebundenen Namens. Wenn S true ist, löst der Versuch, auf den Wert der Bindung vor der Initialisierung zuzugreifen oder ihn nach der Initialisierung festzulegen, immer eine Ausnahme aus, unabhängig von der strengen Moduseinstellung der Operationen, die auf diese Bindung verweisen. S ist ein optionaler Parameter, der standardmäßig false ist.

Kit Sunde
quelle
Wie kann man das sagen, ohne die Quelle zu zitieren? Warum sollte das der Fall sein, constaber nicht für let?
Felix Kling
@FelixKling Welche Aussage muss Ihrer Meinung nach zitiert werden? constlässt sich nicht neu zuweisen (dies wird nicht bestritten) und zeigt in meinem Beispiel deutlich, wie forder variable Definitionsteil entgegen seinen impliziten Erwartungen funktioniert. Der Wert von letkann geändert werden, entspricht funktional vardem angegebenen Beispiel, letist jedoch nicht Teil der Frage.
Kit Sunde
1
Sie meinen also ausdrücklich, dass dies const inur einmal deklariert wird, aber let inicht? Ihr Beispiel zeigt nur, wie es var ifunktioniert, nicht const i. Da es ein klarer Unterschied zwischen ist var, constund letich denke , unter Berufung auf ihn gegen spec constwäre sehr wertvoll sein.
Felix Kling
1
@FelixKling Du verstehst falsch, was ich schreibe. Ich sage, dass alles in diesem Abschnitt der for-Schleife einmal deklariert ist. Dann kann der orthogonale constWert nur einmal zugewiesen werden. Ein erneuter Deklarationsversuch funktioniert constnicht. Mein Beispiel ist es, die Tatsache zu demonstrieren, dass die Deklaration nur einmal erfolgt und die Person, die die Fragen stellt, die Semantik von versteht const. letist nicht das Problem, und nein, ich habe nicht explizit gemeint, was Sie vorschlagen, Sie haben falsch verstanden.
Kit Sunde
3
Kommentar im Chat: Wenn Sie verwenden let, erhält jede Iteration eine eigene Kopie von i. for(let foo = 0; i < 10; ++i){} ist gleichbedeutend mit (funtion() { for(var i = 0; i < 10; ++i){ (function(i) { }(i)) } }()); Hier komme ich her: letund constbeide sind blockumfangig. for/inund for/ofdas gleiche Verhalten für constund letverfügbar machen, die normale forSchleife jedoch nicht. Es wird explizitconst anders behandelt (verständlicherweise vielleicht). Sie sagen einfach, dass es "einmal deklariert" ist, aber das vereinfacht es zu sehr IMO.
Felix Kling