ECMAScript 2015: const w pętlach for

84

Który z poniższych dwóch (lub żaden / oba) fragmentów kodu powinien działać w pełnej implementacji ECMAScript 2015:

for (const e of a)

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

W moim rozumieniu pierwszy przykład powinien działać, ponieważ ejest inicjowany dla każdej iteracji. Czy nie powinno tak być również iw drugiej wersji?

Jestem zdezorientowany, ponieważ istniejące implementacje (Babel, IE, Firefox, Chrome, ESLint) nie wydają się być spójne i mają pełną implementację const, z różnymi zachowaniami dwóch wariantów pętli; Nie jestem też w stanie znaleźć konkretnego punktu w standardzie, więc byłoby to bardzo cenne.

adrianp
źródło
1
const jest dla stałych xx, powinieneś zamiast tego użyć let
Patsy Issa
3
@JamesThorpe Nie, moje pytanie po prostu próbuje wyjaśnić, jakie powinno być zachowanie. Na przykład ESLint uważa pierwszy przykład za OK (i preferowany z prefer-constopcją), a drugi za nieprawidłowy. Większość implementacji przeglądarek uważa oba przykłady za nieprawidłowe.
adrianp
4
Okazuje się, że pierwszy jest OK, ponieważ jest ponownie inicjowany przy każdej iteracji. Działa w Chrome.
lyschoening
@lyschoening i nie powinno to odnosić się również do drugiego przykładu?
adrianp
4
@adrianp zdecydowanie nie jest drugim przykładem. {const i = 0; while(i < a.length) { /* for body */ i += 1}}
Zwykła

Odpowiedzi:

92

Działa następująca pętla for-of:

for (const e of a)

Specyfikacja ES6 opisuje to następująco:

ForDeclaration: LetOrConst ForBinding

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

Imperatyw pętli nie zadziała:

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

Dzieje się tak, ponieważ deklaracja jest oceniana tylko raz przed wykonaniem treści pętli.

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

lizoening
źródło
3
Dlaczego podajesz link do wersji roboczej, a nie do ostatecznej specyfikacji? ecma-international.org/ecma-262/6.0/index.html . Ponadto cytujesz niewłaściwą regułę oceny forpętli. const ...nie jest wyrażeniem. Musisz spojrzeć na regułę for ( LexicalDeclaration Expression ; Expression) Statement .
Felix Kling
@FelixKling nadal miał stary link dodany do zakładek. Masz rację co do zasady oceny. Wniosek powinien być taki sam, ponieważ niezmienne powiązanie jest tworzone dokładnie raz (w kroku 5)?
lyschoening
4
Zgadza się, ale myślę, że wskazówka znajduje się w kroku 9. constNie są one ponownie deklarowane w każdej iteracji.
Felix Kling
4
for (const e of a)NIE wydaje się działać w najnowszej wersji przeglądarki Firefox. DostajęSyntaxError: invalid for/in left-hand side
Chris_F
2
Dokumenty MDN mówią również: „Możesz użyć const zamiast let, jeśli nie zmienisz przypisania zmiennej wewnątrz bloku”. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Phil Gibbins,
45

Nie będę tym razem cytować specyfikacji, ponieważ myślę, że łatwiej jest zrozumieć, co dzieje się na przykładzie.

for (const e of a) …

Jest w zasadzie równoważne z

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

Dla uproszczenia mam ignorowane, że istnieje TDZ z eza awypowiedzi i różne __it.return()/ __it.throw(e)połączenia w przypadku wyjścia pętli przedwcześnie ( breaklub throww organizmie).

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

jest w zasadzie równoważne z

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

W przeciwieństwie dolet , A constdeklaracja w forpętli nie zostanie redeclared w każdej iteracji pętli (a initialiser nie jest ponownie wykonywane w każdym razie). Chyba że breakw pierwszej iteracji, i +=wrzucisz tutaj.

Bergi
źródło
Dla mnie (węzeł 5.0.0) pętla for-of nie działa zgodnie z oczekiwaniami. Po 'let a = [1,3,5], b = []' i 'for (const e a) {b.push (e)}' I get 'b == [1, 1, 1]' . Błąd węzła lub zamierzone zachowanie? Zgodnie z twoim kodem spodziewałbym się nowego przypisania „const e = __res.value” na iterację.
Jürgen Strobel
2
@ JürgenStrobel: Błąd starego węzła, który constma varzasięg podobny do typu i nie rzuca się przy przypisaniu. Użyj trybu ścisłego.
Bergi
Wiem, że to jest stary, ale nieznaczna korekta while ( ( __res = __it.next() ) && !__res.done) {. W przeciwnym razie __resskończy się na byciu truelubfalse
JDB nadal pamięta Monikę
@JDB Dzięki, naprawione! Przy okazji byłoby dobrze, gdybyś to właśnie edytował.
Bergi
3

Twój drugi przykład zdecydowanie nie powinien działać, ponieważ ijest zadeklarowany raz, a nie w każdej iteracji, jest to tylko funkcja tego, jak działa ta kategoria pętli.

Możesz to wypróbować w zwykłej przeglądarce:

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.";
}

Nie jest tak, że constjest to całkowicie zabronione w forpętlach. Tylko forto zmodyfikuje const.

Są one ważne:

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

Te są nieprawidłowe:

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

Nie jestem pewien, dlaczego Firefox podaje SyntaxError po przeczytaniu specyfikacji ES2015 (chociaż jestem pewien, że sprytni ludzie z Mozilli mają rację), wygląda na to, że ma zgłosić wyjątek:

Utwórz nowe, ale niezainicjowane niezmienne powiązanie w rekordzie środowiska. Wartość ciągu N to tekst powiązanej nazwy. Jeśli S ma wartość true, wówczas próby uzyskania dostępu do wartości powiązania przed jego zainicjowaniem lub ustawieniem go po zainicjowaniu zawsze będą zgłaszać wyjątek, niezależnie od ustawienia trybu ścisłego operacji, które odwołują się do tego powiązania. S to opcjonalny parametr, który domyślnie ma wartość false.

Kit Sunde
źródło
Jak możesz to powiedzieć bez podawania źródła? Dlaczego miałoby to mieć miejsce, constale nie let?
Felix Kling
@FelixKling Które stwierdzenie wymaga cytowania? constnie pozwala sobie na ponowne przypisanie (nie jest to kwestionowane), a mój przykład jasno pokazuje, jak fordziała część definicji zmiennej wbrew jego domniemanym oczekiwaniom. Wartość letmożna zmienić, jest funkcjonalnie równoważna z varw podanym przykładzie, ale letnie jest częścią pytania.
Kit Sunde
1
Więc wyraźnie masz na myśli, że const ijest to deklarowane tylko raz, ale let inie chcesz? Twój przykład pokazuje tylko, jak var idziała, a nie const i. Ponieważ istnieje wyraźna różnica między var, consti letmyślę, powołując się wg planu względem constbyłoby bardzo cenne.
Felix Kling
1
@FelixKling Źle czytasz to, co piszę. Mówię, że wszystko w tej sekcji pętli for jest deklarowane raz. Wtedy constwartość ortogonalną można przypisać tylko raz, żadna próba ponownego zadeklarowania constnie zadziała. Mój przykład to pokazanie, że deklaracja zdarza się tylko raz, osoba zadająca pytania rozumie semantykę const. letto nie jest problem i nie, nie miałem na myśli tego, co sugerujesz, źle przeczytałeś.
Kit Sunde
3
Skomentowane na czacie: jeśli używasz let, każda iteracja otrzymuje własną kopię i. for(let foo = 0; i < 10; ++i){} jest odpowiednikiem (funtion() { for(var i = 0; i < 10; ++i){ (function(i) { }(i)) } }()); To jest, skąd pochodzę: leti constoba mają zasięg blokowy. for/ini for/ofujawniają to samo zachowanie dla consti let, ale normalna forpętla nie. To wyraźnie traktuje constinaczej (ze zrozumiałych względów może). Po prostu mówisz, że jest to „raz zadeklarowane”, ale to zbytnio upraszcza IMO.
Felix Kling