変数 の最初の章では、変数を宣言する3つの方法について述べました:

  1. let
  2. const
  3. var

letconst は、レキシカル環境に関してまったく同じように振る舞います。

しかし、var はそれらとは大きく異なり、そしてそれは非常に古くからのものです。一般的には、 var は現在のスクリプトでは使われませんが、古いスクリプトにはまだ潜んでいます。

もしあなたがこのようなスクリプトに出会う予定がなければ、このチャプターをスキップまたは後回しにして構いません。が、後でそれに噛みつかれることがあるかもしれないので注意してください。

一見すると、varlet と同じ振る舞いをします。つまり、変数を宣言します:

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 は未定義

上の通り、varif, 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つのアクションを持っています:

  1. 変数宣言 var
  2. 変数代入 =.

宣言は関数実行の開始時に処理されます(“巻き上げ”)が、代入は常にそれが出現した場所で行われます。従って、コードは本質的にはこのように動作します:

function sayHi() {
  var phrase; // 宣言は最初にされます...

  alert(phrase); // undefined

  phrase = "Hello"; // ...代入 - 実行がここに来た時にされます.
}

sayHi();

すべての var 宣言が関数開始時に処理されるため、どこからでもそれらを参照することができます。しかし、変数は代入されるまで undefined です。

上の両方の例では、 alert はエラーなく動作します。なぜなら変数 phrase が存在するからです。しかし、その値は代入されていないので、 undefied を表示します。

サマリ

var の大きな違いが2つあります:

  1. 変数はブロックスコープを持っておらず、最小のスコープが関数レベルです。
  2. 変数宣言は関数開始時に処理されます。

グローバルオブジェクトに関連する小さな違いがもう少しあります。それは次のチャプターで説明します。

これらの違いは、実際にはほとんどの場合で悪いことです。 まず、ブロックローカル変数を作成することができません。また、巻き上げはエラーを引き起こす余地を増やします。 したがって、新しいスクリプトでは、 var はほとんど使用されません。

チュートリアルマップ

コメント

コメントをする前に読んでください…
  • 自由に記事への追加や質問を投稿をしたり、それらに回答してください。
  • 数語のコードを挿入するには、<code> タグを使ってください。複数行の場合は <pre> を、10行を超える場合にはサンドボックスを使ってください(plnkr, JSBin, codepen…)。
  • 記事の中で理解できないことがあれば、詳しく説明してください。