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では上記の特殊な性質がある為、変数名のオーバーライドは出来るだけ避けたほうが良い。
