次のようなスクリプトがあります:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
しかし、では3
なく、 の両方の時点で警告が表示されます。1
2
i
関数を文字列として記述せずに、を渡す方法はありますか?
ベストアンサー1
タイムアウト関数ごとに「i」の個別のコピーが存在するようにする必要があります。
function doSetTimeout(i) {
setTimeout(function() {
alert(i);
}, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
このようなことを行わない場合 (同じアイデアには他のバリエーションもあります)、各タイマー ハンドラー関数は同じ変数 "i"を共有します。ループが終了したとき、"i" の値は何でしょうか? 3 です。中間関数を使用することで、変数の値のコピーが作成されます。タイムアウト ハンドラーはそのコピーのコンテキストで作成されるため、独自のプライベート "i" を使用できます。
編集:
これまで、タイムアウトをいくつか設定するとハンドラがすべて同時に起動するという事実について、混乱が見られるというコメントがいくつかありました。タイマーを設定するプロセス(の呼び出し
setTimeout()
) にはほとんど時間がかからないことを理解することが重要です。つまり、タイムアウト要求をタイマー キューにインストールするプロセスは非常に高速であるため、システムに「1000 ミリ秒後にこの関数を呼び出してください」と指示すると、ほぼ即座に戻ります。したがって、OP のコードや私の回答の場合のように、タイムアウト要求が連続して行われ、それぞれの時間遅延値が同じである場合、その時間が経過すると、すべてのタイマー ハンドラーが次々に連続して呼び出されます。
ハンドラーを間隔を置いて呼び出す必要がある場合は、 を使用します。
setInterval()
これは とまったく同じように呼び出されますsetTimeout()
が、要求された量の遅延が繰り返された後に複数回実行されます。または、代わりにタイムアウトを確立して、時間値を反復カウンターで乗算することができます。つまり、私のサンプル コードを変更するには、次のようにします。function doScaledTimeout(i) { setTimeout(function() { alert(I); }, i * 5000); }
(
100
ミリ秒のタイムアウトでは効果があまり明らかではないので、数値を 5000 に上げました。) の値はi
基本遅延値で乗算されるため、ループでこれを 5 回呼び出すと、5 秒、10 秒、15 秒、20 秒、25 秒の遅延が発生します。
アップデート
2018 年現在、よりシンプルな代替手段があります。関数よりも狭いスコープで変数を宣言できる新しい機能により、元のコードは次のように変更すれば機能します。
for (let i = 1; i <= 2; i++) {
setTimeout(function() {
alert(i)
}, 100);
}
let
とは異なり、 宣言によって、ループの各反復ごとにvar
個別の が生成されます。i