サイ本読書記 4章 変数

「変数」というと、よく「入れ物」のように例えられてシンプルな内容に思えてましたが、その実中々に深い内容でした。

変数のデータ型

JSでは、JAVAやCのような、『変数のデータ型』が無い。 ので、何でも入れられる。 文字列と数値を連結すると、JSが空気を読んで適切に変換してくれる。 例えば、

var a = "aaa";
var b = 123;
var c = a + b;

alert(c);

こんなコードを実行すると、aaa123 という文字列がアラートで出る。

グローバル変数とローカル変数

変数を宣言するには二つのやり方があって、それはvar文を使うか使わないか。 var文を使わないと、必ずグローバル変数になる。 関数内部でvar文を使って宣言すると、ローカル変数になる。
グローバル変数とローカル変数の違いは、スコープの違い。

スコープとは

有効範囲の事。 ここでは変数の有効範囲。 マークアッパー的には名前空間の雰囲気を想像すると解りやすいかも(Wikiペを見ると厳密には違うと書いてあるけど、雰囲気を掴むとっかかりにはいいと思った)。

で、グローバル変数のスコープはプログラム全体。 ローカル変数のスコープは、その変数が宣言された関数の中だけ。 グローバル変数とローカル変数で名前がかぶっていたら、ローカル変数が優先される。 その時はグローバル変数は『隠れている』という状態になる。

スコープおかわり

スコープについてもう少し。
関数が入れ子になっている場合、それぞれのブロックが別々のスコープを持つような気がするけど、それは無い。 ちなみにブロックは、"{"から"}"までで一つのブロック。 CSSでも宣言ブロックと呼ぶのと同じ感覚。
つまり、関数の中か外か、が問題であって、関数の階層はここでは問題では無い。

スコープもういっちょ

スコープについて更にもう少し。
サイ本のサンプルコードでこんなのがあった。

var scope = "global";
function f(){
 alert(scope);
 var scope = "local";
 alert(scope); 
}
f();

これを実行すると global→local の順にalertが出そうだけど、実際は undefined→local の順。
これは何故かというと、一つ目のalertが実行される時点では、ローカル変数その物は存在しているけどvar文が実行されていないから。
上記のコードは、整理するとこうなる(これも引用)。

function f(){
 var scope;
 alert(scope);
 scope = "local";
 alert(scope);
}
f();

JSはブロックレベルのスコープを持っていない事から、ローカル変数の宣言は関数の最初の方にまとめて行っておいた方が可読性が上がる。

二つのundefined

一つは宣言自体がされていない変数。 これは変数をtypoしたりするとありがちw エラーになる。
もう一つが宣言はされているけど、中身がからっぽの変数。 エラーにはならない。

変数の中身

変数の中に入るのはデータ。 データにはデータ型がある。 このデータ型によって、変数の中に入る物が異なる。
具体例を挙げると、数値や論理値などの場合はデータその物が代入される。 配列やオブジェクトなどの場合はデータを参照する為のキーが代入される。 前者を基本型、後者を参照型と言う。
基本型はまあそのまんま。 参照型がちょっと解りにくいけど、例えば配列だとhoge[0]とかhoge[1]とかで配列の中身を示せる。 この0とか1とかが、データを参照する為のキーになる。
なお、文字列は特殊な扱いになる。 サイ本では

どちらの扱いでもかまわないとしておきます。

との事。

メモリの開放

プログラムという物は、使えば使っただけ、メモリを消費するらしい。 で、Cとかの言語だと、使ったメモリを開放するコードを書かないといけない。 ところがJSは、ガーベジコレクションという技術で、それを自動でやってくれる。

グローバル変数とグローバルオブジェクト

JavaScriptインタプリタが起動されると、コードを実行する前にグローバルオブジェクトの生成が行われる。 このグローバルオブジェクトのプロパティがグローバル変数。 つまり、グローバル変数を宣言するという事は、グローバルオブジェクトのプロパティを定義するという事になる。
トップレベルコード(関数の外のコード)でthisを使うと、グローバルオブジェクトを参照する。

クライアントサイドJSでは、windowオブジェクトがグローバルオブジェクトになる。 windowオブジェクトはwindowプロパティを使用して自身を参照できる。

ローカル変数とCallオブジェクト

対するローカル変数は、Callオブジェクトのプロパティとなる。
グローバル変数とローカル変数で違うオブジェクトのプロパティとする事で、変数名の上書きを防いでいる。

スコープチェーン

ここでもう一度、スコープについて。
スコープチェーンとは、オブジェクトを並べた物。 JSが変数を探す時なんかは、その並び順に沿って調べていく。

グローバル変数の場合は簡単。 グローバルオブジェクトだけなので、そこだけ探せばいい。
ローカル変数は、関数の入れ子がなければ、関数内→グローバルの順に探す。 関数の入れ子があれば、子関数→親関数→グローバルの順に探す。