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

JavaScriptの巻き上げ(ホイスティング)とは?仕組みと動作を実例でわかりやすく解説

巻き上げ(Hoisting)とは何か

JavaScriptにおける巻き上げ(ホイスティング)とは、コードの実行が始まる前に、関数宣言やvarで宣言された変数が、それぞれのスコープの先頭に移動されたかのように扱われる仕組みのことです。この仕組みのおかげで、実際には定義より前の行であっても、関数や変数を呼び出すことができます。

ただし、挙動には注意が必要です。変数の場合、宣言は巻き上げられますが、値の代入は巻き上げられません。そのため、代入前の変数にアクセスするとundefinedが返されます。一方、関数宣言は宣言全体(関数の中身まで)が巻き上げられるため、定義前に呼び出しても正常に実行されます。

varとlet/constの違いに注意

letconstで宣言された変数も技術的には巻き上げられますが、初期化されるまでアクセスできず、この期間は「一時的なデッドゾーン(TDZ:Temporal Dead Zone)」と呼ばれます。TDZ内で変数にアクセスすると、ReferenceErrorが発生します。

サンプルコード

以下のコードでは、変数と関数の巻き上げの挙動を確認できます。varで宣言した変数はundefinedになり、letで宣言した変数はエラーになる様子を比較してみましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JavaScriptの巻き上げ</title>
<style>
    body {
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    }
    .result, .sample {
        font-size: 20px;
        font-weight: 500;
    }
</style>
</head>
<body>
<h1>JavaScriptの巻き上げ(Hoisting)</h1>
<div class="sample">
定義前に関数と変数を呼び出してみる
</div>
<div style="color: green;" class="result"></div>
<button class="btn">ここをクリック</button>
<h3>
上のボタンをクリックすると、巻き上げの動作を確認できます
</h3>
<script>
    let btnEle = document.querySelector(".btn");
    let resEle = document.querySelector(".result");
    btnEle.addEventListener("click", () => {
        // 関数宣言は完全に巻き上げられるため、定義前に呼び出せる
        resEle.innerHTML = "retString() : " + retString() + "<br>";
        // varで宣言した変数はundefinedになる
        resEle.innerHTML += "var a = " + a + "<br>";
        try {
            // letで宣言した変数はTDZによりエラーになる
            resEle.innerHTML += "let b = " + b + "<br>";
        }
        catch (err) {
            resEle.innerHTML += err;
        }
        let b = 55;
        var a = 22;
        function retString() {
            return "Hello world";
        }
    });
</script>
</body>
</html>

実行結果

上記のコードを実行すると、次のような画面が表示されます。

JavaScriptの巻き上げ(ホイスティング)とは?仕組みと動作を実例でわかりやすく解説

「ここをクリック」ボタンを押すと、以下のように結果が出力されます。

JavaScriptの巻き上げ(ホイスティング)とは?仕組みと動作を実例でわかりやすく解説

結果の解説

  • retString():「Hello world」が返される。関数宣言は宣言ごと巻き上げられるため、定義位置より前でも呼び出せます。
  • var aundefinedと表示される。宣言だけが巻き上げられ、値の代入は元の位置で行われるためです。
  • let bReferenceErrorが発生する。letは初期化までの間(TDZ)アクセスできないためです。

まとめ

巻き上げを正しく理解することで、「なぜ定義前の変数がundefinedになるのか」「なぜletだとエラーになるのか」といった挙動の理由が明確になります。現代のJavaScriptでは予期しないバグを避けるためにも、varよりもletconstを使うことが推奨されています。

  1. JavaScriptの「for...in」ステートメントとは?オブジェクトのプロパティをループ処理する方法を解説

    JavaScriptのfor...in文は、オブジェクトが持つすべてのプロパティ(列挙可能なプロパティ)を順番に取り出して処理するためのループ構文です。オブジェクト内の各キー(プロパティ名)にアクセスしながら、対応する値を取得したい場合に非常に便利です。for...inの基本的な構文for (let 変数名 in オブジェクト) { // 各プロパティに対して実行したい処理 }ループ変数には、各反復ごとにオブジェクトのプロパティ名(キー)が文字列として代入されます。値そのものを取得するには、「オブジェクト[変数名]」のようにブラケット記法を使ってアクセスします。サンプルコード以下は、for

  2. 【図解付き】JavaScriptにおけるオブジェクトの等価性をわかりやすく解説

    JavaScriptでは、文字列・数値・真偽値などのプリミティブ型はその「値」に基づいて比較されます。一方、オブジェクト(ネイティブオブジェクトでも独自に定義したカスタムオブジェクトでも)は「参照」に基づいて比較されます。 「参照による比較」とは、複数のオブジェクトがメモリ上の同じ場所を指しているかどうかを判定するという意味です。つまり、プロパティの中身が完全に一致していても、別々に生成された2つのオブジェクトは等しいとみなされません。 押さえておきたいポイント プリミティブ型: 値が同じであれば == や === の比較で true になる オブジェクト型: 同じインスタンス(同一の参照