JavaScriptのvarは、他の言語にない独特な性質がいくつかある。
1.JavaScriptのvarは、Javaの変数宣言と動作が異なる。
2.関数途中でvar 宣言を行った場合でも、変数が作られるのは関数の先頭。
3.変数が作られるのは関数の先頭だが、その変数が初期化は、宣言した位置で行われる。
4.関数内の関数は、変数が作られる時にまとめて作られて初期化される。
5.JavaScriptには、ブロックスコープがない。
これらの特徴が、予期しない落とし穴を作り出すことがある。
次の様なプログラムを考える。
var msg= prompt( 'やあ元気ですか?(貴方の返事を入力して下さい):',"元気" ); if ( msg != null ) { alert( "DBG:あなたの返事:" + msg ); var msg = prompt( msg+ "ですか…それは良かった!(あなたの返事)" ,"最近あれの調子が悪くて…" ); alert( "DBG:あなたの返事:" + msg ); } else { alert( "どうしてなにもいわないのですか?" ); }
ブロックスコープを持つJavaではこの様にブロックの中で変数を宣言する事は稀ではない。JavaScriptでも同様な書き方が可能で、一見すると正しく動作している様に見える。だがここに落とし穴がある。
public class Test { static String hello = "hello"; public static void main(String[] args) { System.out.println( hello ); // "hello"=フィールド参照 String hello = "HELLO"; System.out.println( hello ); // "HELLO"= 変数参照 } }Javaではこの様に外側のスコープを内側のスコープがオーバーライドして同じ名前の変数を隠す事を安全に行うことが出来る。だがJavaScriptではそこに制約がある。
var msg= prompt( 'やあ元気ですか?(貴方の返事を入力して下さい):',"元気" ); (function() { if ( msg != null ) { alert( "DBG:あなたの返事:" + msg ); var msg = prompt( msg+ "ですか…それは良かった!(あなたの返事)" ,"最近あれの調子が悪くて…" ); alert( "DBG:あなたの返事:" + msg ); } else { alert( "どうしてなにもいわないのですか?" ); } })();試してみればわかるが、上記プログラムでは msg は常にnullに判定され、常に「どうしてなにもいわないのですが」が表示される。これこそが正にJavaScriptのvarが持つ「変数は関数先頭で作成される」「初期化は宣言場所で行われる」のふたつの性質がつくりだす落とし穴である。
var msg="hello"; console.log( msg ); // hello (function() { console.log( msg ); // null var msg = "world"; console.log( msg ); // world })();何重にも重ねたクロージャを組み合わせたプログラムを作ると、うっかり外側のクロージャで宣言した変数の名前が、内側のクロージャで宣言した変数の名前と重複してしまうことがある。すると実行環境上では何のエラーも警告も報告されず、予期しない動作をする。この問題は、地味だが検知が非常に難しい。
変数名のオーバーライドを使うと、あるスニペットをコピーペーストで他の部分で流用する時に色々と便利な場合が多い。よって出来るだけ名前を共通にする様にコーディングする場合が多いのではないか。だがJavaScriptでは上記の特殊な性質がある為、変数名のオーバーライドは出来るだけ避けたほうが良い。