JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

JavaScriptのホイスティング(巻き上げ)とは?関数宣言が呼び出し前に使える仕組み

ホイスティング(巻き上げ)とは、JavaScriptにおいて、コードの実行が開始される前に、変数や関数の宣言がそれぞれのスコープの先頭へ移動される動作のことを指します。スコープ内のどこに関数や変数を宣言していても、実行前の段階でそのスコープの先頭に引き上げられるため、宣言より前のコードでも参照できる場合があります。

ただし重要なのは、ホイスティングで移動されるのはあくまで「宣言」だけだという点です。値の代入は元の位置にそのまま残ります。この違いを理解することが、ホイスティングに関する予期せぬバグを防ぐ鍵となります。

関数宣言のホイスティングの例

console.log(functionBelow("Hello"));
function functionBelow(greet) {
    return `${greet} world`;
}
console.log(functionBelow("Hi"));

出力結果

Hello world
Hi world

この例では、関数functionBelowの呼び出しが宣言よりも先に記述されていますが、エラーにならず正常に実行されています。これは関数宣言がホイスティングされるためです。JavaScriptエンジンはコードを実行する前にソース全体を走査し、関数宣言を先にメモリへ登録します。その結果、コード上の記述順序にかかわらず、宣言前に呼び出すことが可能になります。

関数式の場合はホイスティングされない

一方で、関数を変数に代入する形(関数式)で定義した場合には、同じようには動作しません。

console.log(functionBelow("Hello"));
var functionBelow = function(greet) {
    return `${greet} world`;
}
console.log(functionBelow("Hi"));

このコードを実行すると、「functionBelow is not a function」というエラーが発生して失敗します。

さらに、varを取り除いて宣言した場合は、今度は「functionBelow is not defined」というエラーになります。

varで宣言したケースでは、変数自体はホイスティングされてコンテキストに登録されますが、代入はまだ行われていないため、値はundefinedのままです。この挙動は「変数のホイスティング」と呼ばれます。この特性により、JavaScriptでは無名関数やアロー関数は決してホイスティングされません。

let・constでの宣言との違い

補足として、letやconstで宣言した変数も実際にはホイスティングされます。しかし、初期化が完了するまでの間は「一時的デッドゾーン(TDZ:Temporal Dead Zone)」と呼ばれる状態になり、この間にアクセスするとReferenceErrorが発生します。varとは異なりundefinedが返ることはありません。こうした挙動の違いから、現代のJavaScript開発では、意図しない動作を避けるためにletやconstの使用が推奨されています。

  1. JavaScriptの関数プロトタイプとは?仕組みと使い方を解説

    JavaScriptで作成されたすべての関数には、JavaScriptエンジンによって自動的にprototypeプロパティが追加されます。このprototypeプロパティはオブジェクトであり、デフォルトではconstructorプロパティを含んでいます。関数のプロトタイプには、以下のようにしてアクセスできます。functionName.prototypeコンストラクタ関数を使ってオブジェクトを生成する場合、このprototypeプロパティを活用することで、同じコンストラクタから作成された複数のオブジェクト間でメソッドやプロパティを効率的に共有することができます。これにより、各インスタンスごとに

  2. JavaScriptでのメソッド借用:call()・apply()・bind()の使い方を解説

    JavaScriptでは、call()、apply()、bind()という3つのメソッドを使うことで、あるオブジェクトが持つメソッドを別のオブジェクトが「借用」できます。これは、本来そのメソッドを持たないオブジェクトに対して、thisの参照先を差し替えてメソッドを実行できる便利なテクニックです。 3つのメソッドの違い call():引数をカンマ区切りで個別に渡し、即座に関数を実行します。 apply():引数を配列(または配列風オブジェクト)として渡し、即座に関数を実行します。 bind():thisを固定した新しい関数を返すだけで、即座には実行されません。返された関数は後から好きなタイミン