2013-01-01 30 views
6

Here's an exampleループ変数が別のスコープにないため、単純なJSループが期待どおりに動作しない状況が発生します。Javascript関数スコープForループ

しばしば提示ソリューションは、次のようになりますループコードの不快に見えるビットを構築することである。

for (var i in obj) { 
    (function() { 
     ... obj[i] ... 
     // this new shadowed i here is now no longer getting changed by for loop 
    })(i); 
} 

私の質問は、これを大幅に改善することができますか?

Object.prototype.each = function (f) { 
    for (var i in this) { 
     f(i,this[i]); 
    } 
}; 

// leading to this somewhat more straightforward invocation 
obj.each(
    function(i,v) { 
     ... v ... 
     // alternatively, v is identical to 
     ... obj[i] ... 
    } 
); 

私は「スコープループ」必要があることを確認:私はこれを使用してもらえますか?これは、見た目がやや綺麗で、通常のfor-loopと同じようなパフォーマンスを持つはずです(同じ方法で使用するので)。

更新:Object.prototypeでやっていることは、かなり壊れていますので、かなり大きなものです。すべて。ここで

は控えめ実装です:

function each (obj,f) { 
    for (var i in obj) { 
     f(i,obj[i]); 
    } 
} 

非常にわずか

each(obj, 
    function(i,v) { 
     ... v ... 
    } 
); 

への呼び出しの変更は、だから私はjQueryのは、それをこのようにしている場合、私は自分の質問に答えてきたと思います、することができます本当に間違っています。私が見落とした問題は答えが必要です。

+1

「Object.each()」シムの実装方法についてJSライブラリをチェックしてください。パフォーマンスと最適化については、[JSPerf](http://jsperf.com)を使用してコードをテストし、[CodeReview](http://codereview.stackexchange.com/)でお問い合わせください。 – Joseph

+0

はい私は見ました'jQuery.each()'は今すぐ実装されているので、引数の順序を単純に逆転させ、実際には何もチェックしません。 –

答えて

1

あなたの答えはかなりカバーしていますが、何らかの理由でeach()機能が便利でないときは、通常のforループを使用するのが妥当なので、元のループの変更は注目に値すると思います。

更新:example referenced by the questionに似た例を使用して、さまざまなアプローチを比較するように変更しました。 each()関数は、反復処理を行うために実装された配列が必要であるため、この例を調整する必要がありました。次のセットアップと仮定

var vals = ['a', 'b', 'c', 'd'], 
    max = vals.length, 
    closures = [], 
    i; 

質問からの例を使用して、元のループは2つの機能は、各反復中に作成されるため(nは反復の数である)、2N関数を作成終わる:

for (i = 0; i < max; i++) { 
    closures[i] = (function(idx, val) { // 1st - factoryFn - captures the values as arguments 
     return function() {    // 2nd - alertFn - uses the arguments instead 
      alert(idx + ' -> ' + val); //     of the variables 
     }; 
    })(i, vals[i]); 
} 

これはループが開始される前に、一度工場の機能を作成し、それを再利用することによってのみ、N + 1の関数を作成することを低減することができる。

var factoryFn = function(idx, val) { 
    return function() { 
     alert(idx + ' -> ' + val); 
    }; 
}; 

for (i = 0; i < max; i++) { 
    closures[i] = factoryFn(i, vals[i]); 
} 

これは、この状況でeach()関数がどのように使用されるかとほぼ同等であり、合計でn + 1個の関数が作成されます。ファクトリ関数は一度作成され、すぐに引数としてeach()に渡されます。

each(vals, function(idx, val) { 
    closures[idx] = function() { 
     alert(idx + ' -> ' + val); 
    }; 
}); 

FWIW、私はeach()を使用する利点は、コードが少し短くなっていると思うし、それがeach()関数に渡された権利としてファクトリ関数を作成明確に、これはその使用のみで示しています。forループバージョンIMOのメリットは、ループが正しく機能するようなコードなので、each()関数が別のファイルで定義されているか、他の人が作成した関数などのように完全に透過的です。

+0

これはループが不必要に非同期になると思います。それは明らかに回避することができます競争条件のようなnastiesを引き起こす可能性があります。 –

+0

'setTimeout()'の使用のために?私はこれを例として使用しています。私はあなたがリンクしている例を見ませんでした...どのように私はそれを見逃すことはできませんでした。私はあなたが質問で言及したようなものを使うように答えを改訂しました。あるいは、あなたのコメントは 'setTimeout()'以外のものを参照していますか? – tiffon

+0

うん。 TBH私は本当にあなたがなぜそれを使っているのか見ていないと思うのですが、実際にはsetTimeout()を使ってたくさんのクールなことをしていますが、私はそのコメントを書いたのですが、あなたの答えをもっと徹底的に読んでいたはずです。 –

1

グローバルスコープ

何かがグローバルであるとは、コード内のどこからでもアクセスできることを意味します。たとえば、次のようにします。

var monkey = "Gorilla";そのコードは、Webブラウザで実行されていた場合

function greetVisitor() { 

return alert("Hello dear blog reader!"); 
} 

、関数スコープは、このようにそのWebブラウザウィンドウで実行されているすべてのものにそれが

利用できるように、ウィンドウになります。

ローカルスコープ

グローバルスコープとは対照的に何かが単にコードの

特定の部分で定義され、アクセスされたときに、ローカルスコープは関数のように、です。例えば;

関数talkDirty(){

var saying = "Oh, you little VB lover, you"; 
return alert(saying); 
} 

alert(saying); // Throws an error 

あなたは上記のコードを見てみた場合、変数のことわざはtalkDirty

関数内でのみ使用可能です。それの外側には、まったく定義されていません。注意が必要です:

の前にvarキーワードを付けて宣言すると、自動的にグローバル変数になります。

関数SAVENAME(firstNameの){

機能capitalizeName:

これは何も意味することは、ネストされた機能を持っている場合、内側の関数は

含む機能変数や関数へのアクセス権を持っているということです(){

return firstName.toUpperCase(); 

} VAR)が(= capitalizeName大文字。

return capitalized; 

}

alert(saveName("Robert")); // Returns "ROBERT" 

あなたがちょうど見たように、内側の関数capitalizeNameが送られてきた任意のパラメータが必要ですが、外側のSAVENAME関数のパラメータのfirstNameへの完全な

のアクセスを持っていませんでした。明確にするために、の別の

例を見てみましょう:

関数兄弟(){

var siblings = ["John", "Liza", "Peter"]; 

機能siblingCount(){

var siblingsLength = siblings.length; 

return siblingsLength; 

}

関数joinSiblingNames(){

return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n"); 

}

return joinSiblingNames(); 

}

alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter" 

あなたがちょうど見たように、両方の内部の機能が含む関数で兄弟配列へのアクセス権を持っている、と

各内部関数がアクセス権を持っています同じレベルの他の内部関数(この場合、

joinSiblingNamesはsiblingCountにアクセスできます)。ただし、siblingCountの変数siblingsLengthは、その関数内でのみ使用可能な

です。