- ベストアンサー
forの中でsetTimeoutする時のスコープ
下記のようなソースで上手くsetTimeoutが効きません。 for(var i = 0; i < 5; i++){ target = obj[i]; setTimeout(function (){ target.css('background-image', 'fuga'); }, 100 * i); } デバッグで見てみると、どうも変数targetが空になっているようのですが、for文の中でsetTimeoutは不可なのでしょうか? jqueryの$.eachを使うとうまくいったのですが、、、
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
関数内のtargetは実行時に外のtargetを参照します つまりそれまでにtargetが変わってしまっていた場合思った通りに実行されません このコード以外にtargetを弄る部分がないとしても、 5回呼ばれる関数内でのtargetは5回ともobj[4]を指します なぜなら実行時にはtargetはobj[4]となっているからです つまり結論をいうとその書き方ではダメです 治すパターンはおそらく5つくらいはありますが、 setTimeoutの場合第三以降の引数を使うといいでしょう for(var i = 0; i < 5; i++){ target = obj[i]; setTimeout(function (target){ target.css('background-image', 'fuga'); }, 100 * i, target); } こうすれば毎ループ毎に期待通りのtargetが関数に渡されるようセットされ、 関数内のtargetはその渡されるtargetの方を参照するのでうまく行きます
その他の回答 (1)
setTimeoutの中にある無名関数は、このfor文が実行されている関数とは別のものですよね? この関数内にある関数ではないわけですね。なぜって、setTimeoutが呼ばれたらその関数が実行されるわけではなくて、指定した時間が経過後に呼び出されるコールバックなわけですから。 ということは、この無名関数が呼び出されたとき、既にforのある関数は実行が終了していて、その中で宣言された変数も全て開放されていると考えないといけないでしょう。 従って、一番わかりやすい解決法は、obj配列をグローバル変数として保管しておき、無名関数内ではそのグローバル変数を参照して処理する、というものでしょう。jQueryがうまく動いたのも、あれはjQueryオブジェクトというグローバルオブジェクト内にすべての情報を保管するように設計されているからじゃないでしょうか。 あるいは、引数としてオブジェクトを渡すという手もあります。ただしこの場合、ちょっと注意が必要です。 setTimeout(function(){ func(target); } function func(target){ …… } こんな感じで、別途setTimeout時に呼び出される処理を関数にまとめておき、無名関数内からそれを引数付きで呼び出す、といった形にしないとうまくいかないでしょう。