ECMAScript 2015:forループの定数


83

以下の2つの(またはどちらでもない/両方の)コードフラグメントのどちらが、完全なECMAScript2015実装で機能する必要がありますか。

for (const e of a)

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

私の理解では、最初の例はe反復ごとに初期化されるため、機能するはずです。これiは、2番目のバージョンにも当てはまるはずではありませんか?

既存の実装(Babel、IE、Firefox、Chrome、ESLint)は一貫性がなくconst、2つのループバリアントのさまざまな動作を伴う、の完全な実装を持っているように見えるため、私は混乱しています。また、基準の具体的なポイントがわからないので、よろしくお願いします。


1
constは定数xx用であり、代わりにletを使用する必要があります
Patsy Issa

3
@JamesThorpeいいえ、私の質問は単に動作がどうあるべきかを明らかにしようとしています。たとえば、ESLintは、最初の例をOK(およびprefer-constオプションで優先)と見なし、2番目の例を無効と見なします。ほとんどのブラウザ実装は、両方の例を無効と見なします。
adrianp 2015

4
最初のものは、反復ごとに再初期化されるため、問題ありません。Chromeで動作します。
lyschoening 2015

@lyschoeningとこれは2番目の例にも当てはまりませんか?
adrianp 2015

4
@adrianpは間違いなく2番目の例ではありません。通常のforループは、基本的に次と同等です{const i = 0; while(i < a.length) { /* for body */ i += 1}}
2015

回答:


91

次のfor-ofループが機能します。

for (const e of a)

ES6仕様では、これを次のように説明しています。

ForDeclaration:LetOrConst ForBinding

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

命令型のforループは機能しません。

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

これは、ループ本体が実行される前に宣言が1回だけ評価されるためです。

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


3
最終仕様ではなくドラフトにリンクするのはなぜですか?ecma-international.org/ecma-262/6.0/index.html。また、forループの評価ルールが間違っていると言っています。const ...式ではありません。のルールを確認する必要がありますfor ( LexicalDeclaration Expression ; Expression) Statement
Felix Kling 2015

@FelixKlingにはまだ古いリンクがブックマークされていました。あなたは評価ルールについて正しいです。不変のバインディングが1回だけ作成されるため(ステップ5で)、結論は同じである必要がありますか?
lyschoening 2015

4
そうですが、手がかりはステップ9にあると思いますconst。sは反復ごとに再宣言されません。
Felix Kling 2015

4
for (const e of a)Firefoxの最新バージョンでは動作しないようです。私が得ているSyntaxError: invalid for/in left-hand side
Chris_F 2016

2
MDNドキュメントには、「ブロック内で変数を再割り当てしない場合は、letの代わりにconstを使用することもできます」とも記載されています。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
フィルギビンズ

44

例によって何が起こるかを理解する方が簡単だと思うので、今回は仕様を引用しません。

for (const e of a) …

基本的には同等です

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

簡単にするeために、a式にTDZがあり、ループが途中で(または本体で)終了する場合のさまざまな__it.return()/__it.throw(e)呼び出しがあることを無視しました。breakthrow

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

基本的には同等です

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

対照的にletconstで宣言forループは、すべてのループ反復で再宣言されません(および初期化子は、とにかく再実行されません)。break最初の反復でない限り、i +=ここにスローします。


私(ノード5.0.0)の場合、for-ofループが期待どおりに機能していません。'let a = [1,3,5]、b = []' and'for(const e of a){b.push(e)} 'の後に' b == [1、1、1] ' 。ノードのバグ、または意図された動作?あなたのコードによると、反復ごとに新しい 'const e = __ res.value'割り当てを期待します。
ユルゲンストローベル2015年

2
@JürgenStrobel:古いノードのバグconstがあるvar様範囲をして割り当てにスローされません。厳密モードを使用します。
ベルギ2015年

私はこれが古いことを知っていますが、わずかな修正:while ( ( __res = __it.next() ) && !__res.done) {。それ以外の場合は__resされて終わるtruefalse
JDBはまだモニカ覚えて

@JDBありがとう、修正しました!ところで、あなたがそれを編集しただけなら私は大丈夫だっただろう。
ベルギ

3

2番目の例iは、宣言が1回であり、各反復で宣言されていないため、確実に機能しないはずです。これは、そのカテゴリのループがどのように機能するかの関数にすぎません。

これは通常のブラウザで試すことができます。

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

ループでconst完全に禁止されているわけではありませんforforconstを変更するのはそれだけです。

これらは有効です:

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

これらは無効です:

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

FirefoxがES2015仕様を読んだ後にSyntaxErrorを出す理由はわかりませんが(Mozillaの賢い人は正しいと確信していますが)、例外が発生することになっているようです。

環境レコードに、新しいが初期化されていない不変のバインディングを作成します。文字列値Nは、バインドされた名前のテキストです。Sがtrueの場合、初期化される前にバインディングの値にアクセスしようとしたり、初期化後に設定しようとすると、そのバインディングを参照する操作の厳密なモード設定に関係なく、常に例外がスローされます。Sはオプションのパラメーターで、デフォルトはfalseです。


ソースを引用せずにそれをどのように言うことができますか?なぜそうなるconstのにそうではないのletでしょうか?
Felix Kling 2015

@FelixKlingどのステートメントを引用する必要があると思いますか?const自分自身を再割り当てすることはできません(これは争われていません)。私の例forでは、彼の暗黙の期待に反して、変数定義部分がどのように機能するかを明確に示しています。の値はlet変更できます。機能的にvarは上記の例と同じletですが、質問の一部ではありません。
キットSunde 2015

1
つまりconst i、それlet iは一度だけ宣言されるということですが、そうではありませんか?あなたの例はvar i、どのように機能するかを示しているだけで、ではありませんconst i。そこの間に明確な違いがあるのでvarconstそしてlet、私は彼がに関してスペック引用することは考えてconst非常に価値があるであろう。
Felix Kling 2015

1
@FelixKlingあなたは私がタイプしているものを読み間違えています。forループのそのセクションのすべてが一度宣言されていると言っています。その場合、直交const値は1回しか割り当てることができず、再宣言しようとしてconstも機能しません。私の例は、宣言が1回だけ発生し、質問をする人がのセマンティクスを理解しているという事実を示すことですconstletそれは問題ではありません、そして私はあなたが提案していることを明確に意味していませんでした、あなたは読み間違えました。
キットSunde 2015

3
チャットでのコメント:を使用するlet場合、各反復は独自ののコピーを取得しますi。 これは私がどこから来たのかfor(let foo = 0; i < 10; ++i){} と同等 (funtion() { for(var i = 0; i < 10; ++i){ (function(i) { }(i)) } }());です:letそしてconst両方ともブロックスコープです。for/infor/ofに同じ行動を公開constしてletますが、通常のforループにはありません。それは明示的にconst異なった扱いをします(当然のことながら多分)。あなたはそれが「一度宣言される」と単に言うが、それはそれをあまりにも単純化するIMO。
Felix Kling 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.