Tatsuya Oiwa About Archive Feed Github Twitter

JavaScriptのスコープについて

スコープとは、変数が参照可能な範囲のことである。

静的スコープと動的スコープ

スコープには大きく二つの種類があり、一つは、静的スコープ(構文スコープ)、もう一つは動的スコープと呼ばれる。JavaScriptを含む、ほぼ全ての言語においては、静的スコープが使われている。

静的スコープでは、プログラムのコンパイル処理におけるトークナイズ(および構文解析)によってスコープが決定される。つまり、変数や関数などが、プログラム内のどの場所に記述されているかによってスコープが決まる。

ただし、JavaScriptでは、例外的にeval(..)withを使って動的スコープを生成することもできる。しかし、これらのスコープはコンパイル時に最適化されずパフォーマンスが低下してしまうため、できる限り使用するべきではない。

LHS参照とRHS参照

変数の参照には、変数に値を与える参照と、変数から値を取得する参照の二種類がある。変数を与える参照はLHS参照(Left Hand Side)と呼ばれ、変数から値を取得する参照はRHS参照(Right Hand Side)と呼ばれる。

例えば、var a = 2;というコードを実行するとき、内部では以下の二つのステップに分割されている。

  1. var aによって、スコープ内に変数を宣言する。この処理はコードが実行される前に行われる。
  2. a = 2によって、変数が参照(LHS参照)される。もし変数が宣言されていれば、変数に値が代入される。この処理はコードの実行時に行われる。

LHS参照とRHS参照はともに、その参照が実行されているスコープから始まり、もし参照しようとした変数が見つからない場合、そのスコープの外側のスコープ(親のスコープ)で同様に参照を続ける。この参照を、最も外側のスコープ(グローバルスコープ)まで繰り返していく。

もし、RHS参照時に変数が見つからなければReferenceError例外が発生する。一方、LHS参照時に変数が見つからない場合、コンパイラは自動的にグローバル変数を作成する(ただしStrict Mode - use strict使用時の場合はRHS参照と同様にReferenceErrorとなる)。

例えば、以下のようなコードの場合、

function foo(a) {
  var b = a;
  return a + b;
}

var c = foo(2);

LHS参照は、c = ..a = 2b = ..の計三つ、RHS参照はfoo(2..= a;a + .... + bの計四つとなる。

ホイスティング(Hoisting)

JavaScriptのエンジンは、var a = 2;を二つの命令var aa = 2に分割し、var aをコンパイル時に、a = 2を実行時にそれぞれ処理する。そのため、次のようなコードの場合、

a = 2;

var a;

console.log( a );

console.log(a);の出力結果は、undefinedではなく2となる。これは、JavaScriptのエンジンが上記のコードを内部で次のように解釈しているためである。

var a;
a = 2;

console.log(a);

このように、変数の宣言が、スコープ内で記述されている順番に関わらず最初に行われることをホイスティング(hoisting - 巻き上げ)と呼ぶ。