実際、この状況は非常に簡単に説明でき、JavaScript だけでなく多くのプログラミング言語で発生します。
a,b,c,c,c,c
最初に出力シーケンスを取得し、最初の 3 つの要素を破棄しましょうa,b,c
。これはf()
、最初のfor
ループ内で関数を実行したときに出力されるものだからです。
したがって、配列c,c,c
を反復処理すると出力されるシーケンスが残ります。印刷されfs
ない理由は、最初のループ内にあるパラメーターがクロージャーによって参照によってキャプチャされるためです (ただし、定義が有効かどうかはわかりません) (JavaScript のすべての関数はいくつかのクロージャーであることを思い出してください)。選別)。a,b,c
x
for
f()
これで、console.log(x)
を呼び出すときに式が評価されるため、キャプチャされたパラメーターf()
の現在のx
値が引数として渡されます。予想どおり、コードは機能しますがf()
、最初のfor
ループ内で呼び出すと、x
が から割り当てられたばかりなarr[i]
ので、 が得られますa,b,c
。しかし、ループを終了すると、x
(外側のスコープでは使用できなくても) によってキャプチャされf()
ますが、最初のループの後に値c
(最後の反復で取得した値) があり、それが評価されるときに評価されます。関数を 2 回繰り返します。
実際、これは多くの紛らわしいバグの原因となる可能性があるため、一部の言語ではこれを検出して開発者に通知できます (たとえば、C# コンパイラは、このような構造を検出すると、次の警告を生成します: Access to modified closure
)。
この問題を解決するには、最初のループ内の現在の値をキャプチャする必要があります。x
for
for (var i in arr) {
var x = arr[i];
var f = (function(x) {
return function() { console.log(x) };
})(x);
f();
fs.push(f);
}
ここでは、IIFE (Immediately-Invoked Function Expression) を使用して の現在の値をキャプチャしますx
。
お役に立てれば。