JavaScriptの型とスコープ

これからしばらく備忘録もかねて、JavaScript関連の話題でも書いてみようかと*1 *2。まずはJavaScriptの型とスコープについて*3


まず、JavaScriptの予約語にも書いたとおり、予約語としてintdoubleといった、C系列の言語を知っている人には馴染み深い型を表す予約語が一通り揃っている。でも、これらは現在のJavaScriptでは使用されていない。なぜなら、現在のJavaScriptには変数に型がなく、値に型がある言語となっているからだ。
JavaScriptでは、基本的には変数のスコープとして大域(グローバル)スコープと関数内局所スコープしかなく、ブロックスコープは存在しない。これは、関数内でvarを使って宣言された変数*4は関数内局所スコープとなり、その関数内であればどこでも使用できることを意味する。以下のコードを例に考えてみよう*5

var x = 0;
function func(a) {
    x = a;  // グローバル変数xにaを代入?
    for (var x = 0; x < 3; x++) {
        alert(x);   // alert関数はアラートダイアログを出す関数
    }
}
func(10);
alert("global x is " + x);

実行してみる

var x = 0;
function func(a) {
    x = a;  // グローバル変数xにaを代入
    for (var i = 0; i < 3; i++) {
        alert(i);
    }
}
func(10);
alert("global x is " + x);

実行してみる
さて、JavaScriptを切っていたりする人のために結果を説明すると、最初の例では0、1、2、global x is 0と順にアラートが出たはずだ。しかし次の例では、0、1、2、global x is 10となったはずである。
これらの違いはfor文の変数の名前がグローバル変数と同じかどうかだけである。JavaScriptにはブロックスコープがないので、最初の例のfor文で作った変数xは関数内局所スコープとなり、その関数内ならどこでもアクセス出来てしまう。よってx = a;xはなんとグローバル変数ではなく、関数内で有効な変数xを指していることになる。
更に、

function func1(a) {
    // グローバル変数xを関数内で作っている?
    x = a;
    for (var x = 0; x < 3; x++) {
        alert(x);
    }
}
function func2(a) {
    // グローバル変数xを関数内で作っている
    x = a;
}

func1(10);
// これはエラー
//alert(x);

func2(10);
// これはエラーじゃない
alert(x);

実行してみる
こんなことも起こりうる。


まとめると、JavaScriptにはブロックスコープはなく、ブロックスコープを前提にプログラムを組むとひどい目に合うということ。なにやらJavaScript2.0ではブロックスコープが導入されるらしいけど、いまだにIE5.0を使っている人がいるこの状況ではもうしばらくブロックスコープの恩恵にあずかれそうにない。とりあえずの擬似スコープとして、(function () {...})()を使う方法とwithを応用する方法があるが、それはまた機会があれば。


今回はid:m-hiyama:20051209を参考にさせて頂きました。より詳しく載っています。

*1:あまり深くは突っ込まないで、実践的な部分のみを説明するつもり。だから自然と何かしらの言語を知っていることが前提。

*2:JavaScriptのバージョンはtry-catchが使えるという理由だけで1.3にしようかと。

*3:型の話はほとんどしてないけど・・・

*4:JavaScriptでは変数に型が無いので、変数を宣言するときはvar(variableの略。変数のこと)キーワードを使用する。更に、varを省略するとグローバル変数になる。

*5:ここでは、JavaScriptでは関数はfunctionを使用して宣言し、戻り値を指定しないところだけ分かればいい。