JavaScriptは、シンプルな仕様が美しい言語です。
しかしながら、シンプルが故に、一度罠にハマると見つけにくくて抜け出すのが大変です。
罠にハマった時に、すぐに抜け出せるように、簡単な例で抑えておきましょう。
ある一つのJavaScriptの罠が入ったコード
罠が入ったサンプルコードがこちら。
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
コンソールに i という変数を出力しています。
出力はどのようになりますか?
これ、実行したらどのような出力になると思います?
0、1、2と答えたあなた。わたしはそんな素直なあなたが大好きです。
2、2、2と答えたあなた。素晴らしい。JavaScriptを極めてますね!ただ、不正解。ケアレスミス。
出力の答え
こたえは3、3、3。不思議ですね。
さて、なぜこうなるかを解説しましょう。
罠の解説
罠の本質。クロージャとsetTimeout
JavaScriptにはクロージャという仕組みがあります。簡単に言うと、変数が生きつづける仕組みです。
これとsetTimeout という処理を後から実行する仕組みのコンビネーションでこれが発生します。
処理の解説
for 分の中で変数 i は0、1、2と増えて行きます。
しかしながらconsole.logへの出力はsetTimeout の中で行われるため、for文のループが回っている最中は実行されません。なのでconsole.log(i)の i は、ループ中はまだ値として解釈されません。
iが3になってループが終わり、setTimeout が実行される順番が来るとそこで始めて i の中身が出力されるので、出力は3,3,3となるのです。
予言しよう!あなたはいつか、この罠を踏む。
クロージャもsetTimeoutも、JavaScriptには欠かせない重要な機能です。
しかしながら、よくよく理解していても、この罠をちょくちょくふむことがあります。
「あれー。なんかループの中のデータがおかしい!」とか、「ん?変数の値が上書きされてる!」とかいう事態が起きたら、この投稿を思い出してください。
サンプルコードは、ループとsetTimeout で例にあげましたが、この罠はその他の組み合わせ、クリックと通信処理など、他の組み合わせでも発生します。