引用されたコードは、リストしたことを実行しません。おそらく、質問のために単純化する何かを失ったでしょう。
ただし、このコードには、説明している効果があります。
for(j in groups){
doSomething(function() {
// Callback for `doSomething`
console.log(j, somefunction(groups[j]));
});
}
ここで、関数を呼び出すdoSomething
だけでなく、関数 (何を行うためのコールバック) も作成していることに注意してください。
これが発生する理由は、関数が作成されたときの変数のコピーではなく、作成される関数が変数 (および実行コンテキスト内の他のすべて)への永続的な参照を持っているためです。その実行コンテキストに対するクロージャーと呼ばれます。したがって、ループが実行され、一連のことを開始するための一連の呼び出しが行われ、後でコールバックが呼び出されると 3 になるため、すべて同じになります。j
doSomething
j
通常の解決策は、次のように、呼び出される前に変更されないものを閉じる関数を作成することです。
for(j in groups){
doSomething(makeCallback(j));
}
function makeCallback(jarg) {
return function() {
console.log(jarg, somefunction(groups[jarg]));
};
}
現在、jarg
コールバックで ではなく を使用していますj
。jarg
は への呼び出しのコンテキストの一部であるためmakeCallback
、関数を作成したときと後で呼び出されるときの間で変更されません。
データを関数にバインドするもう 1 つの方法は、Function#bind
(バックグラウンドで効果的にクロージャーを作成する) を使用することです。これは、V8 エンジンにあるため、NodeJS で使用できる ES5 機能です。これがどのように見えるかです:
for(j in groups){
doSomething(function(jarg) {
// Callback for `doSomething`
console.log(jarg, somefunction(groups[jarg]));
}.bind(this, j)); // <== Note the bind
}
またはそれほど混乱しない(そしておそらくより効率的に):
for(j in groups){
doSomething(handler.bind(this, j));
}
function handler(jarg) {
// Callback for `doSomething`
console.log(jarg, somefunction(groups[jarg]));
}
閉鎖についての詳細: