web-dev-qa-db-ja.com

ECMAScript 2015:forループのconst

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

for (const e of a)

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

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

既存の実装(Babel、IE、Firefox、Chrome、ESLint)には一貫性がないようで、2つのループバリアントのさまざまな動作を伴うconstの完全な実装があるため、混乱しています。また、標準の具体的なポイントを見つけることができないので、それは大歓迎です。

66
adrianp

次の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)

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

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

81
lyschoening

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

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)呼び出しを無視しました。ループは途中で終了します(本体のbreakまたはthrow)。

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

基本的にと同等です

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

letとは対照的にconstループ内のfor宣言は、ループの繰り返しごとに再宣言されません(初期化子は再作成されません-とにかく実行されます)。最初の反復でbreakをしない限り、_i +=_はここにスローされます。

35
Bergi

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ループでforが完全に禁止されているわけではありません。 constを変更するのはforのみです。

これらは有効です:

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に設定されるオプションのパラメーターです。

3
Kit Sunde