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ż e
jest inicjowany dla każdej iteracji. Czy nie powinno tak być również i
w 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.
javascript
for-loop
scope
constants
ecmascript-6
adrianp
źródło
źródło
prefer-const
opcją), a drugi za nieprawidłowy. Większość implementacji przeglądarek uważa oba przykłady za nieprawidłowe.{const i = 0; while(i < a.length) { /* for body */ i += 1}}
Odpowiedzi:
Działa następująca pętla for-of:
for (const e of a)
Specyfikacja ES6 opisuje to następująco:
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
źródło
for
pętli.const ...
nie jest wyrażeniem. Musisz spojrzeć na regułęfor ( LexicalDeclaration Expression ; Expression) Statement
.const
Nie są one ponownie deklarowane w każdej iteracji.for (const e of a)
NIE wydaje się działać w najnowszej wersji przeglądarki Firefox. DostajęSyntaxError: invalid for/in left-hand side
Nie będę tym razem cytować specyfikacji, ponieważ myślę, że łatwiej jest zrozumieć, co dzieje się na przykładzie.
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
e
zaa
wypowiedzi i różne__it.return()
/__it.throw(e)
połączenia w przypadku wyjścia pętli przedwcześnie (break
lubthrow
w organizmie).jest w zasadzie równoważne z
{ const i = 0; while (i < a.length) { … i += 1; } }
W przeciwieństwie do
let
, Aconst
deklaracja wfor
pętli nie zostanie redeclared w każdej iteracji pętli (a initialiser nie jest ponownie wykonywane w każdym razie). Chyba żebreak
w pierwszej iteracji,i +=
wrzucisz tutaj.źródło
const
mavar
zasięg podobny do typu i nie rzuca się przy przypisaniu. Użyj trybu ścisłego.while ( ( __res = __it.next() ) && !__res.done) {
. W przeciwnym razie__res
skończy się na byciutrue
lubfalse
Twój drugi przykład zdecydowanie nie powinien działać, ponieważ
i
jest 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
const
jest to całkowicie zabronione wfor
pętlach. Tylkofor
to 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:
źródło
const
ale nielet
?const
nie pozwala sobie na ponowne przypisanie (nie jest to kwestionowane), a mój przykład jasno pokazuje, jakfor
działa część definicji zmiennej wbrew jego domniemanym oczekiwaniom. Wartośćlet
można zmienić, jest funkcjonalnie równoważna zvar
w podanym przykładzie, alelet
nie jest częścią pytania.const i
jest to deklarowane tylko raz, alelet i
nie chcesz? Twój przykład pokazuje tylko, jakvar i
działa, a nieconst i
. Ponieważ istnieje wyraźna różnica międzyvar
,const
ilet
myślę, powołując się wg planu względemconst
byłoby bardzo cenne.const
wartość ortogonalną można przypisać tylko raz, żadna próba ponownego zadeklarowaniaconst
nie zadziała. Mój przykład to pokazanie, że deklaracja zdarza się tylko raz, osoba zadająca pytania rozumie semantykęconst
.let
to nie jest problem i nie, nie miałem na myśli tego, co sugerujesz, źle przeczytałeś.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ę:let
iconst
oba mają zasięg blokowy.for/in
ifor/of
ujawniają to samo zachowanie dlaconst
ilet
, ale normalnafor
pętla nie. To wyraźnie traktujeconst
inaczej (ze zrozumiałych względów może). Po prostu mówisz, że jest to „raz zadeklarowane”, ale to zbytnio upraszcza IMO.