JavaScript のホイスティング
ホイスティング(Hoisting / 巻き上げ)とは、宣言をスコープの先頭に移動させるJavaScriptのデフォルトの挙動です。
1. JavaScriptの宣言はホイスティングされる
JavaScriptでは、変数を使用した後でその変数を宣言することが可能です。
言い換えれば、変数を宣言する前にその変数を使用できるということです。
以下の 例1 は、例2 と全く同じ結果をもたらします。
1.1 例 1
x = 5; // xに5を代入
elem = document.getElementById("demo"); // 要素を取得
elem.innerHTML = x; // 要素にxを表示
var x; // xを宣言1.2 例 2
var x; // xを宣言
x = 5; // xに5を代入
elem = document.getElementById("demo"); // 要素を取得
elem.innerHTML = x; // 要素にxを表示これを理解するには、「ホイスティング」という用語を理解する必要があります。
ホイスティングとは、すべての宣言を現在のスコープ(現在のスクリプトまたは現在の関数)の先頭に移動させるJavaScriptの標準的な動作を指します。
2. let と const キーワードの挙動
let および const で定義された変数もブロックの先頭にホイスティングされますが、**初期化(Initialized)**はされません。
これはつまり、コードブロックはその変数の存在を認識していますが、実際に宣言されるまでその変数を使用することはできないということを意味します。let 変数を宣言前に使用しようとすると、ReferenceError(参照エラー)が発生します。
変数はブロックの開始から宣言されるまでの間、「Temporal Dead Zone(TDZ:一時的死区)」に置かれます。
2.1 let のエラー例
以下のコードは ReferenceError になります:
carName = "Volvo";
let carName;const 変数を宣言前に使用することは構文エラー(Syntax error)となるため、コード自体が実行されません。
2.2 const のエラー例
このコードは実行されません:
carName = "Volvo";
const carName;3. JavaScriptの初期化はホイスティングされない
JavaScriptがホイスティングするのは宣言(Declarations)のみであり、初期化(Initializations)は対象外です。
そのため、以下の 例1 と 例2 は同じ結果にはなりません。
3.1 例 1
var x = 5; // xを初期化
var y = 7; // yを初期化
elem = document.getElementById("demo"); // 要素を取得
elem.innerHTML = x + " " + y; // xとyを表示3.2 例 2
var x = 5; // xを初期化
elem = document.getElementById("demo"); // 要素を取得
elem.innerHTML = x + " " + y; // xとyを表示
var y = 7; // yを初期化例2において、y が undefined(未定義)になるのはなぜでしょうか?
それは、宣言(var y)のみが先頭にホイスティングされ、初期化(= 7)は元の位置に残るためです。
ホイスティングのおかげで、y は使用される前に宣言はされていますが、初期化はホイスティングされないため、y の値は undefined として扱われます。
例2は、実質的に以下のコードを書いているのと同じことになります:
3.3 インタープリタによる解釈のイメージ
var x = 5; // xを初期化
var y; // yを宣言(ホイスティングされた結果)
elem = document.getElementById("demo"); // 要素を取得
elem.innerHTML = x + " " + y; // xとyを表示
y = 7; // yに7を代入4. 変数は必ずスコープの先頭で宣言する!
ホイスティングは、多くの開発者にとって未知であったり、見落とされがちな挙動です。
ホイスティングを正しく理解していないと、プログラムにバグ(エラー)が紛れ込む原因になります。
バグを避けるための最良の方法は、常にすべての変数を各スコープの冒頭で宣言することです。
これはJavaScriptが実際にコードを解釈する仕組みに基づいているため、常に推奨される優れたルールです。
なお、JavaScriptを Strict Mode(厳格モード) で実行している場合、宣言されていない変数を使用することは許可されません。