🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

7回答

1855閲覧

JavaScriptのfor文の中での関数実行

退会済みユーザー

退会済みユーザー

総合スコア0

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

3クリップ

投稿2019/12/18 03:01

JavaScriptのfor文の中での関数実行に関する質問です
質問は、なぜ関数がfor文の回数分実行されるのかについてです。

参考サイトhttps://qiita.com/ukiuni@github/items/463493a690265cec8bb7

javascript

1for (var i = 0; i < 3; i++) { 2 setTimeout(function() { 3 console.log(i); 4 }, 0); 5}

様々なサイトで、for文実行中は関数が実行されずに、終了後にiの最後の値を参照して実行されるという記述を見ました。しかし、for文が終了した後に、関数の実行回数がfor文の回る回数と同じになっていることが理解できません。for文実行中に、○○回回ったという情報がどこかに記憶されて、for文終了後に関数を実行する際、iは最後の値だけを見て、回数はその記憶から引っ張り出してくるから、iは最後の値のまま5回回るという結果になるのでしょうか?
クロージャなどがまだ理解できず、すごく単純に以下のように考えています。
・for文が終わったのであれば、関数の実行は1回だけのはず。なぜならforという繰り返し処理が終わっているのだから
・しかし実際はfor文のブロックの中に関数がいるので、関数は繰り返し処理に含まれている
・繰り返し処理に含まれていて、関数の外にある変数も参照できるのあれば、iも参照できるはずで、iを参照できるなら、iの数が変わったことも関数内でわかるはず
なぜ関数はfor文の回数分実行されるのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2019/12/18 03:07

単にforの回数だけsetTimeoutが実行されている処理にしか見えませんけど…
Takumiboo

2019/12/18 03:40

for文の中では関数は実行されないなんてどこに書いてあるんですか…。
guest

回答7

0

どんな処理が順番に実行されるか、流れを簡単な言葉で記します。

  1. イベントループ開始
  2. i = 0 (for文開始)
  3. setTimeout実行 0秒後のイベントループ(0なので今回は次)で実行されるようにタスクキューとして溜め込む
  4. i++
  5. setTimeout実行 0秒後のイベントループにタスクキューとして溜め込む
  6. i++
  7. setTimeout実行 0秒後のイベントループにタスクキューとして溜め込む
  8. i++ (for文終了)
  9. やることなくなったら、次のイベントループまで待機
  10. イベントループ開始
  11. タスクキューがあるのでコールスタックに追加
  12. 以下コールスタックを実行
  13. console.log(i) 1回目のsetTimeoutで登録されたタスクを実行
  14. console.log(i) 2回目のsetTimeoutで登録されたタスクを実行
  15. console.log(i) 3回目のsetTimeoutで登録されたタスクを実行

以上、こんな感じの動作になっています。

投稿2019/12/18 04:12

編集2019/12/18 04:17
so87

総合スコア764

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

setTimeoutは「何ミリ秒にこの関数を実行する」予定を積む関数です。そして、予定として積まれた各関数は、同じiを参照しますので、実行前にiがループを抜けきって5となっていれば、その5を表示します。

これに対して、ループ変数をletで宣言した場合、それはループ1回ごとに別個の変数として扱われますので、setTimeoutの中のiが紐づくのも、ループ1回ごとに確保された変数となりますので、登録時のiの値がそのまま残ることとなります。

投稿2019/12/18 04:10

maisumakun

総合スコア145975

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2019/12/18 04:12 編集

JavaScriptはシングルスレッドですので、同期的な処理が続いている間はsetTimeoutの中身の関数は実行されません。forループが長引いても、各関数の実行はループの終了より後です。
maisumakun

2019/12/18 04:34 編集

addEventLitenerは同じ関数を複数回登録してもイベント1回につき同じ関数は1度しか実行されませんが、setTimeoutを使って同じ関数を複数回登録すると、登録した回数だけ実行されます。
kei344

2019/12/18 05:16

> addEventLitenerは同じ関数を複数回登録しても 質問文のようなコードの場合複数回登録したコードが、登録した回数だけ実行されませんか?「同じ関数」と書くなら例示しないとわかりにくいと思います。 https://jsfiddle.net/wtezu6yh/1/
guest

0

【JavaScript入門】setTimeoutの使い方とサンプル事例まとめ!

0ミリ秒と指定されたsetTimeoutはすぐに実行されるように思いますが、先にメインの実行処理が行なわれるのです。

ただこういう理由なだけです。
その時の変数の動きについてはご提示のQiitaに書かれている通りですね。

投稿2019/12/18 03:44

Takumiboo

総合スコア2536

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

貼っていただきました参考サイトでは
for文の中でsetTimeoutを使った場合のケースを説明しており、
for文の実行後にsetTimeoutが3回実行されるため
すでに変数iには3が入っているので、3の出力が3回あると記載されています。

for文実行中は関数が実行されず」
という解釈は勘違いかと思います。

投稿2019/12/18 03:40

編集2019/12/18 03:43
e-suzuno

総合スコア74

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

e-suzuno

2019/12/18 04:20

私自身混乱を招いてしまってますが 関数という主語が大きいせいで、 setTimeout関数 と表記をしていればもう少しわかりやすかったかもしれません。
guest

0

○○回回ったという情報がどこかに記憶されて

for の初期化部分(initialization)で宣言されている i に記憶されています。

・for文が終わったのであれば、関数の実行は1回だけのはず。

forの繰り返しで、setTimeout() を 3回実行しており、時限実行する関数 は3つ設定されています
(3つとも、for が終わってから実行されるように予定されます)。

iを参照できるなら、iの数が変わったことも関数内でわかるはず

時限実行する関数は、外側のブロックにある変数 i を参照しますが、
for の初期化部分(initialization)で

  1. var 宣言されたものだと、実行時の値を参照します(3, 3, 3)。
  2. let 宣言されたものだと、for ループのブロック内の記述に応じた値を参照します(0, 1, 2)。

javascript

1// let で宣言した場合 2for (let i = 0; i < 3; i++) { 3 setTimeout(function() { 4 console.log(i); 5 }, 0); 6}

この違いは スコープブロック を理解すると疑問も解消できるのではないかと思います。


クロージャなどがまだ理解できず

クロージャは、変数を var 宣言しかできなかった時代に、外部から変数の値を書き換えられることがないようにする 特殊な関数生成の手法です。古いコードを読み解く時には必要ですが、let, const が使える現代は、慌てて覚える必要は無くなっていると思います。
局所変数宣言のletと 定数宣言のconst を先に覚えましょう。

投稿2019/12/18 08:40

AkitoshiManabe

総合スコア5434

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

for ループで、3回setTimeoutが呼び出されるので、「0秒後にfunction内の処理を実行する」というタイマーが3個セットされることになります。直感的には、0秒後なのでforループ中にすぐに実行されるように思えますが、JavaScriptの仕様上そうはなりません。forループを抜けた後に3個のタイマーが実行されます。これがforループを抜けた後に関数が3回実行される要因となります。
また、関数内で表示されるiの値は、forループを抜けた後なので3になっています。

投稿2019/12/18 04:33

memememomo

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

setTimeoutが外の変数を参照しているからですね
おそらく非同期プログラムの最適化の恩恵だと思います

よくあるのはvarをletにする処理ですが

javascript

1for(let i = 0; i < 3; i++) { 2 setTimeout(()=>{ 3 console.log(i); 4 },0); 5}

きちんと引数で渡せば参照されます

javascript

1for(var i = 0; i < 3; i++) { 2 console.log(x); 3 },0,i)); 4}

また、きちんと非同期処理を管理すれば引数で渡さなくても大丈夫です

javascript

1(async()=>{ 2for(var i = 0; i < 3; i++) { 3 await new Promise(resolve=>setTimeout(()=>{ 4 resolve(i); 5 },0)).then(console.log); 6} 7})(); 8

※あっぷされない

投稿2019/12/18 03:43

編集2019/12/18 04:09
yambejp

総合スコア116694

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2019/12/18 04:09 編集

ここ2-3日ちょっとながい回答が全然アップできない 本文が更新できないのでレスでいくつかサンプル
yambejp

2019/12/18 04:10

(1)letで処理 for(let i = 0; i < 3; i++) { setTimeout(()=>{ console.log(i); },0); }
yambejp

2019/12/18 04:10

(2)引数で渡す for(var i = 0; i < 3; i++) { console.log(x); },0,i)); }
yambejp

2019/12/18 04:10

(3)同期で処理する (async()=>{ for(var i = 0; i < 3; i++) { await new Promise(resolve=>setTimeout(()=>{ resolve(i); },0)).then(console.log); } })();
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問