variables の最初の章では、変数宣言の3つの方法について述べました:
let
const
var
let
と const
はレキシカル環境に関して全く同じように振る舞います。
しかし、var
は大きく異なる獣で、それは非常に古くから始まっています。一般的に var
は現在のスクリプトでは使われませんが、古いのもの中に潜んでいます。
もしあなたがこのようなスクリプトに出会う予定がなければ、このチャプターをスキップまたは後回しにして構いません。しかし、後でそれに噛みつかれることがあるかもしれません。
一目見る限りだと、var
は let
と同じ振る舞いをします。つまり、変数を宣言します:
function sayHi() {
var phrase = "Hello"; // ローカル変数, "var", "let" の代わり
alert(phrase); // Hello
}
sayHi();
alert(phrase); // Error, phrase は未定義です
…しかし、ここには違いがあります。
“var” はブロックスコープを持ちません
var
変数は関数全体かグローバルかのいずれかであり、ブロックを通して見ることができます。
例:
if (true) {
var test = true; // "let" の代わりに "var" を使う
}
alert(test); // true, if の後も変数は生きています
もし2行目で let test
を使った場合、alert
では見えません。しかし var
はコードブロックを無視するので、グローバルの test
を取得します。
ループでも同様です: var
はブロック、またはループのローカルにはなれません:
for (var i = 0; i < 10; i++) {
// ...
}
alert(i); // 10, "i" ループ後も見え、それはグローバル変数です
コードブロックが関数の内側にある場合、var
は関数レベルの変数になります:
function sayHi() {
if (true) {
var phrase = "Hello";
}
alert(phrase); // works
}
sayHi();
alert(phrase); // Error: phrase は未定義
上の通り、var
は if
, for
もしくは他のコードブロックを貫通します。それは、長い間JavaScriptでは、ブロックがレキシカル環境を持っていなかったためです。そして、 var
はそれを想起させます。
“var” は関数の開始で処理されます
var
宣言は、関数の開始時(またはグローバルのスクリプト開始時)に処理されます。
言い換えると、var
変数は関数の最初で定義され、定義される場所は関係ありません(定義がネストされた関数ではないと言う仮定で)。
従って、このようなコード:
function sayHi() {
phrase = "Hello";
alert(phrase);
var phrase;
}
…は技術的にはこれと同じです(var phrase
を上に移動させています):
function sayHi() {
var phrase;
phrase = "Hello";
alert(phrase);
}
…もしくはこれです(コードブロックが無視されることを忘れないでください):
function sayHi() {
phrase = "Hello"; // (*)
if (false) {
var phrase;
}
alert(phrase);
}
また、すべての var
が関数の先頭に “持ち上げられ” ているので、人々はそのような振る舞いを “巻き上げ” とも呼びます。
しががって、上の例では if (false)
の分岐は決して実行されませんが、それは関係ありません。その内側の var
は関数の最初に処理されるので、(*)
の時点で変数は存在します。
宣言は巻き上げられますが、代入はされません。
次のコードはその例です:
function sayHi() {
alert(phrase);
var phrase = "Hello";
}
sayHi();
行 var phrase = "Hello"
はその中で2つのアクションを持っています:
- 変数宣言
var
- 変数代入
=
.
宣言は関数実行の開始時に処理されます(“巻き上げ”)が、代入は常にそれが出現した場所で行われます。従って、コードは本質的にはこのように動作します:
function sayHi() {
var phrase; // 宣言は最初にされます...
alert(phrase); // undefined
phrase = "Hello"; // ...代入 - 実行がここに来た時にされます.
}
sayHi();
すべての var
宣言が関数開始時に処理されるため、どこからでもそれらを参照することができます。しかし、変数は代入されるまで undefined です。
上の両方の例では、 alert
はエラーなく動作します。なぜなら変数 phrase
が存在するからです。しかし、その値は代入されていないので、 undefied
を表示します。
サマリ
var
の大きな違いが2つあります:
- 変数はブロックスコープを持っておらず、最小が関数レベルで見えます。
- 変数宣言は関数開始時に処理されます。
グローバルオブジェクトに関連する小さな違いがもう少しあります。それは次のチャプターで説明します。
これらの違いは、実際にはほとんどの場合で悪いことです。 まず、ブロックローカル変数を作成することができません。また、巻き上げはエラーを引き起こす余地を増やします。 したがって、新しいスクリプトの場合、 var
は非常にまれにしか使用されません。
コメント
<code>
タグを使ってください。複数行の場合は<pre>
を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。