概要: このチュートリアルでは、JavaScript のホイストとその内部動作について学びます。
JavaScript ホイスト入門
JavaScript エンジンが JavaScript コードを実行すると、グローバル実行コンテキストが作成されます。グローバル実行コンテキストには、以下の 2 つのフェーズがあります。
- 作成
- 実行
作成フェーズでは、JavaScript エンジンは変数と関数の宣言をコードの先頭に移動します。これは、JavaScript のホイストとして知られています。
変数のホイスト
変数のホイストとは、JavaScript エンジンが変数宣言をスクリプトの先頭に移動することです。たとえば、次の例では、counter 変数を宣言し、その値を 1 に初期化しています。
console.log(counter); // 👉 undefined
var counter = 1;Code language: JavaScript (javascript)この例では、宣言の前に counter 変数を参照しています。
しかし、最初の行のコードはエラーを引き起こしません。その理由は、JavaScript エンジンが変数宣言をスクリプトの先頭に移動するためです。
技術的には、実行フェーズではコードは次のようになります。
var counter;
console.log(counter); // 👉 undefined
counter = 1;
Code language: JavaScript (javascript)グローバル実行コンテキストの作成フェーズでは、JavaScript エンジンは変数 counter をメモリに配置し、その値を undefined に初期化します。
let キーワード
次は、let キーワードを使用して変数 counter を宣言しています。
console.log(counter);
let counter = 1;Code language: JavaScript (javascript)JavaScript は次のエラーを発行します。
"ReferenceError: Cannot access 'counter' before initializationエラーメッセージは、counter 変数が既にヒープメモリにあることを説明しています。しかし、まだ初期化されていません。
内部的には、JavaScript エンジンは let キーワードを使用する変数宣言をホイストします。ただし、let 変数は初期化しません。
存在しない変数にアクセスしようとすると、JavaScript は別のエラーをスローすることに注意してください。
console.log(alien);
let counter = 1;Code language: JavaScript (javascript)エラーは次のとおりです。
"ReferenceError: alien is not definedCode language: plaintext (plaintext)関数のホイスト
変数と同様に、JavaScript エンジンは関数宣言もホイストします。これは、JavaScript エンジンが関数宣言もスクリプトの先頭に移動することを意味します。例えば:
let x = 20,
y = 10;
let result = add(x, y);
console.log(result); // 👉 30
function add(a, b) {
return a + b;
}Code language: JavaScript (javascript)出力
30この例では、add() 関数を定義する前に呼び出しています。上記のコードは、次のコードと同等です。
function add(a, b){
return a + b;
}
let x = 20,
y = 10;
let result = add(x,y);
console.log(result); // 👉 30Code language: JavaScript (javascript)実行コンテキストの作成フェーズでは、JavaScript エンジンは add() 関数宣言をヒープメモリに配置します。正確には、JavaScript エンジンは Function 型のオブジェクトと、関数オブジェクトを参照する関数参照 add を作成します。
関数式
次の例では、add を通常の関数から関数式に変更しています。
let x = 20,
y = 10;
let result = add(x,y); // ❌ Uncaught ReferenceError: add is not defined
console.log(result);
let add = function(x, y) {
return x + y;
}Code language: JavaScript (javascript)コードを実行すると、次のエラーが発生します。
Uncaught ReferenceError: add is not definedCode language: plaintext (plaintext)グローバル実行コンテキストの作成フェーズでは、JavaScript エンジンはメモリに add 変数を作成し、その値を undefined に初期化します。
次のコードを実行すると、add は undefined であるため、関数ではありません。
let result = add(x,y);Code language: JavaScript (javascript)add 変数は、グローバル実行コンテキストの実行フェーズでのみ匿名関数に割り当てられます。
アロー関数
次の例では、add 関数式をアロー関数に変更しています。
let x = 20,
y = 10;
let result = add(x,y); // ❌ Uncaught ReferenceError: add is not defined
console.log(result);
let add = (x, y) => x + y; Code language: JavaScript (javascript)アロー関数は関数式を定義するための糖衣構文であるため、このコードも関数式の例と同じエラーを発行します。
Uncaught ReferenceError: add is not definedCode language: plaintext (plaintext)関数式と同様に、アロー関数はホイストされません。
まとめ
- JavaScript のホイストは、実行コンテキストの作成フェーズで発生し、変数と関数の宣言をスクリプトの先頭に移動します。
- JavaScript エンジンは、
letキーワードを使用して宣言された変数をホイストしますが、varキーワードで宣言された変数のように初期化しません。 - JavaScript エンジンは、関数式とアロー関数をホイストしません。