JavaScript でのホイスティング
ホイストは、変数と関数の宣言が、含まれるスコープ (グローバル スコープまたは関数スコープ) の先頭に移動 (または「ホイスト」) される動作です。コードが実行されます。これは、コード内で実際に宣言される前に変数や関数を使用できることを意味します。
変数での巻き上げ
変数
- var で宣言された変数はスコープの先頭にホイストされますが、その値はコード内の代入が発生するポイントまで初期化されません。
console.log(x); // undefined
var x = 5;
console.log(x); // 5
let と const
- let および const で宣言された変数もホイストされますが、宣言に達するまでは「一時的なデッド ゾーン」に置かれます。宣言される前にアクセスすると、ReferenceError が発生します。
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;
// block scope
{
let x = 5;
}
console.log(x); // ReferenceError: x is not defined
関数でのホイスティング
伝統的な機能
- 関数宣言は完全にホイストされます。つまり、宣言と関数本体の両方がスコープの先頭に移動されます。これにより、コード内で関数を宣言する前に関数を呼び出すことができます。
sayHello(); // "Hello!"
function sayHello() {
console.log("Hello!");
}
- 対照的に、関数式 (関数が変数に代入されている) は変数としてホイストされるだけなので、変数が初期化される前に関数式を呼び出すと、未定義または TypeError が発生します。
greet(); // TypeError: greet is not a function
var greet = function () {
console.log("Hi!");
};
greet(); // ReferenceError: Cannot access 'one' before initialization
let greet = function () {
console.log("Hi!");
};
アロー関数
- 対照的に、関数式 (関数が変数に代入されている) は変数としてホイストされるだけなので、変数が初期化される前に関数式を呼び出すと、未定義または TypeError が発生します。
greet(); // TypeError: greet is not a function
var greet = () => {
console.log("Hi!");
};
greet(); // ReferenceError: Cannot access 'one' before initialization
let greet = function () {
console.log("Hi!");
};
時間的デッドゾーン (TDZ)
時間的デッド ゾーン (TDZ) は、let と const を使用して宣言された変数に存在します。これは、JavaScript が、変数が宣言されて初期化される前にこれらの変数にアクセスできないように設計されているためです。
ホイスティング時に var と let、const が異なる動作をする理由
- それは JavaScript の歴史的な進化によるものです。
- 当初、JavaScript は開発者ではないユーザー向けに設計されており、JavaScript の主な中心的な使用法は、Web ページに小さなインタラクティブな要素を追加することでした。
- したがって、var は機能スコープのみをサポートします。また、当時はブロックスコープはありませんでした。
- しかし、その後の JavaScript の進化では、var の操作やバグの修正がより複雑になりました。
- JavaScript を他の最新言語と競争できるようにするために、let、const、アロー関数、ES6 メソッドなどの機能が追加されました。
var が let や const のように更新されない理由
- 下位互換性のためです。
- 当時、JavaScript は多くの企業で広く使用されていたため、既存の機能を更新または変更すると、コードベースが破壊されることになります。
- したがって、最新の機能が個別に追加されました。
面接でよくある質問
- JavaScript におけるホイスティングとは何ですか?
- JavaScript では何が巻き上げられ、何が巻き上げられないのでしょうか?
- ホイスティングに関して、var、let、const の違いは何ですか?
- JavaScript の時間的デッド ゾーン (TDZ) とは何ですか?
- 関数宣言と関数式によるホイスティングを説明できますか?
- ES6 モジュールのホイスティングとは何ですか?
- 実際のコードではホイスティングに依存することを避けるべきなのはなぜですか?
まとめ
- ホイストは JavaScript のデフォルトの動作で、コンパイル段階で変数と関数の宣言がそれぞれのスコープの先頭に移動されます。
- ホイスティングは、var および従来の関数で宣言された変数に対してのみ機能し、let、const、および arrow 関数では機能しません。
- 関数宣言のみがホイストされるため、従来の関数は動作しますが、関数が変数に代入されている場合、関数が定義されるまで呼び出し可能になりません。
- var 関数と従来の関数がホイストされるのに let、const、arrow 関数がホイストされない理由は、初期段階では JavaScript が主に小規模な UI インタラクションに使用されていたためです。
- しかしその後、企業によるアプリケーション構築に JavaScript が広く使用されるようになり、グローバル スコープだけでバグを修正することが難しくなりました。
- したがって、将来のアップデートでは、より安全な懸念が解決されることになります。
- さらに、既存の機能を更新するとコードベースが壊れる可能性があるため、新しい機能は別途追加されました。