web-dev-qa-db-ja.com

forループにキーワードを入れます

ECMAScript 6のletは、頭痛を引き起こすことなくブロックスコープを提供することになっています。以下のコードで、関数のiが現在の反復の値ではなく、ループの最後の値(varと同様)に解決される理由を説明できますか?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    things["fun" + i] = function() {
        console.log(i);
    };
}

things["fun0"](); // prints 3
things["fun1"](); // prints 3
things["fun2"](); // prints 3

[〜#〜] mdn [〜#〜] によると、そのようなletループでforを使用すると、ループ本体のスコープ内の変数がバインドされます。ブロック内で一時変数を使用すると、期待どおりに動作します。なぜそれが必要なのですか?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    let index = i;
    things["fun" + i] = function() {
        console.log(index);
    };
}

things["fun0"](); // prints 0
things["fun1"](); // prints 1
things["fun2"](); // prints 2

Traceurとnode --harmonyを使用してスクリプトをテストしました。

41

squintの答えはもはや最新ではありません。 ECMA 6仕様 では、指定された動作は

for(let i;;){}

iは、ループの反復ごとに新しいバインディングを取得します。

これは、すべてのクロージャーが異なるiインスタンスをキャプチャすることを意味します。したがって、012の結果は、現時点では正しい結果です。これをChrome v47 +で実行すると、正しい結果が得られます。IE11およびEdgeで実行すると、現在、誤った結果(333)が生成されているようです。

このバグ/機能に関する詳細情報は、 このページ ;のリンクにあります。

let式が使用されると、すべての反復が前のスコープまでチェーンされた新しいレキシカルスコープを作成するためです。これは、 here と報告されるlet式の使用にパフォーマンスに影響します。

50
neuron

このコードを Babel に渡したので、使い慣れたES5の観点から動作を理解できます。

_for (let i = 0; i < 3; i++) {
    i++;
    things["fun" + i] = function() {
        console.log(i);
    };
    i--;
}
_

ES5にトランスコードされたコードは次のとおりです。

_var _loop = function _loop(_i) {
    _i++;
    things["fun" + _i] = function () {
        console.log(_i);
    };
    _i--;
    i = _i;
};

for (var i = 0; i < 3; i++) {
    _loop(i);
}
_

2つの変数が使用されていることがわかります。

  • 外側のスコープのiは、反復するにつれて変化する変数です。

  • 内部スコープの__i_は、各反復の一意の変数です。最終的には__i_の3つの個別のインスタンスが存在します。

    各コールバック関数は、対応する__i_を参照でき、必要に応じて、他のスコープの__i_ sとは無関係に操作することもできます。

    (コールバック内でconsole.log(i++)を実行すると、3つの異なる__i_ sがあることを確認できます。以前のコールバックで__i_を変更しても、後のコールバックからの出力には影響しません。)

各反復の終わりに、__i_の値がiにコピーされます。したがって、反復中に一意の内部変数を変更すると、外部反復変数に影響します。

ES6がWTFJSの長年の伝統を引き継いでいることは喜ばしいことです。

14
joeytwiddle

私見-最初にこのLETを実装したプログラマー(初期バージョンの結果を作成)は、正気に関して正しくそれを行いました。彼らはその実装中に仕様を一目見なかったかもしれません。

単一の変数が使用されているが、forループにスコープされていることはより意味があります。特に、ループ内の条件に応じてその変数を自由に変更できるためです。

しかし、待ってください-あなたはcanループ変数を変更します。 WTFJS !!ただし、内部スコープで変更しようとすると、今は動作しませんbecauseこれは新しい変数です。

私がしなければならないことが好きではない私が望むものを得るために(forに対してローカルな単一の変数):

{
    let x = 0;
    for (; x < length; x++)
    {
        things["fun" + x] = function() {
            console.log(x);
        };
    }
}

反復ごとに新しい変数を処理するために、より直感的な(想像上の場合)バージョンを変更する場合:

for (let x = 0; x < length; x++)
{
    let y = x;
    things["fun" + y] = function() {
        console.log(y);
    };
}

Y変数を使用した私の意図が何であるかは明らかです。または、SANITYが宇宙を支配した場合はそうでした。

最初の例はFFで機能するようになりました。 0、1、2が生成されます。問題は修正されました。私はこの問題をWTFJSと呼びます。

追伸私のWTFJSへの言及は、上記のJoeyTwiddleからのものです。今日前に知っておくべきミームのように聞こえますが、今日はそれを学ぶ絶好の機会でした。

1
Gerard ONeill