関数宣言と変数宣言は、JavaScript インタープリターによって常にそれらを含むスコープの先頭に暗黙的にホイストされます。以下の記事ではJavaScriptのホイスティング(変数ホイスティングと関数宣言ホイスティング)に関する情報を中心に紹介していますので、困っている方はぜひ参考にしてみてください。
この記事では主に JavaScript のホイスティング (変数ホイスティングと関数宣言ホイスティング) に関連する内容を紹介し、参考と学習のために共有します。以下では多くを述べませんが、詳細な紹介を見てみましょう。
関数宣言/変数をスコープの先頭に「移動」する方法。
ホイスティングという用語は、識別子の解析を説明するために多くの JavaScript ブログ投稿で使用されています。実際、ホイスティングという言葉は、変数や関数の宣言が関数またはグローバル スコープの先頭にどのようにホイストされるかを説明するために使用されます。この用語はどの JavaScript ドキュメントにも記載されておらず、私たちがホイスティングと呼んでいるものは、その文字通りの意味を比喩として使用しているだけです。
JavaScript のスコープの仕組みについてすでに基本を理解している場合は、ホイスティングをより深く理解することで、より強力な基盤を構築することができます。 (Fool's Wharf からのメモ: JavaScript の一般的な概念として、変数ホイスティングと関数宣言ホイスティングは、フロントエンド開発の面接でよく質問されたり、フロントエンド開発の筆記試験の問題に出題されます。これは、ホイスティング (ホイスティング) を理解することの重要性を示しています。 .)
基本をよりよく理解するために、「ホイスティング」が実際に何を意味するのかを確認してみましょう。また、JavaScript はインタープリタ型言語であり、コンパイル済み言語とは異なります。つまり、JS コードが 1 行ずつ実行されることを意味します。
次の例を考えてください:
console.log(notyetdeclared); // 打印 'undefined' var notyetdeclared = 'now it is declared'; hoisting(); function hoisting(){ console.log(notyetdeclared); // 打印 'undefined' var notyetdeclared = 'declared differently'; console.log(notyetdeclared); // 打印 'declared differently' }
上記のコード例を分析した後、いくつかの質問をしてください:
6 行目、なぜこの関数が宣言される前にアクセスできるのでしょうか?
1行目ではエラーがスローされていませんが、この時点では変数notyetdeclaredが存在しないためでしょうか?
4 行目で notyetdeclared がグローバル スコープで宣言されていますが、9 行目で出力されたときにまだ未定義なのはなぜですか?
JavaScript は非常に論理的であり、これらの奇妙な問題にはすべて明確な説明があります。
上から始めて、JavaScript でコードが実行されるときに実行コンテキストが確立されることを説明しましょう。 JavaScript には、グローバル実行コンテキストと関数実行コンテキストという 2 つの主なタイプの実行コンテキストがあります (Fool's Pier 注: 実行コンテキストは、私たちが通常話しているコンテキストとは異なるという事実に特に注意してください。実行コンテキストはスコープを指し、通常のコンテキストは this が指す値です)。 JavaScript はシングルスレッド実行モデルに基づいているため、一度に実行できるコードは 1 つだけです。
上記のコードのプロセスは、図に示すとおりです:
上記のサンプルコードのコールスタック:
プログラムはスタック上のグローバル実行コンテキストから実行を開始します。 。
hoisting() 関数が呼び出されると、新しい関数実行コンテキストがスタックにプッシュされ、グローバル実行コンテキストが一時停止されます。
hoisting() の実行が完了すると、hoisting() 実行コンテキストがスタックからポップされ、グローバル実行コンテキストが復元されます。
この手順は一目瞭然ですが、サンプル コードの実行時に発生した例外については実際には説明されていません。実行コンテキストがコードの実行を追跡するのに対し、字句環境は識別子の特定の変数へのマッピングを追跡します。字句環境は基本的に JavaScript のスコープ メカニズムの内部実装です。通常、字句環境は、コードの関数や for ループ ブロックなど、JavaScript コードの特定の構造に関連付けられます。関数が作成されるたびに、その関数が作成された字句環境への参照が [[Environment]] という内部プロパティに渡されます。
これらの用語はすべて、シンプルかつ非常に論理的な概念をカバーしています。壊れるのを許してください。字句環境は、コード ブロック内の変数と関数を追跡するために使用される興味深い名前です。各字句環境は、ローカル変数、関数宣言、パラメータの追跡に加えて、その親字句環境も追跡します。したがって、上記のサンプル コードは JavaScript エンジンで次のように解析されます。上記のコードの語彙環境は図に示すとおりです。
注:
理解に問題がある場合は、次の 3 つの記事を確認してください:
JavaScript のスコープとコンテキスト
JavaScript のスコープとクロージャの中心概念
JavaScript スコープの分析例
字句環境で識別子を解決するために、JavaScript エンジンは現在の環境への参照をチェックします。参照が見つからない場合は、[[環境]] を使用して外部環境に移動します。これは、識別子が見つかるか、「未定義」エラーがスローされるまで続きます。
基本的に、JavaScript コードの実行は 2 つの段階に分かれています。最初のフェーズでは、すべての変数と関数の宣言を現在の字句環境に登録します。完了すると、JavaScript 実行の第 2 フェーズが始まります。
フェーズ 1 について詳しく説明すると、2 つのステップで動作します。
現在の関数宣言内のコードをスキャンします。関数式とアロー関数はスキップされます。検出された関数ごとに、新しい関数が作成され、関数名を使用して環境にバインドされます。識別子の名前がすでに存在する場合、その値は上書きされます。
次に、現在の環境の変数をスキャンします。 var で定義された変数や、他の関数の外に配置された変数を検索し、値が unknown に初期化された識別子を登録します。識別子が存在する場合、値は変更されません。
注: let および const で定義されたブロック変数は、var とは少し異なります。これについては別の記事で詳しく説明します。
これで、字句環境の基本概念をある程度理解できたはずなので、サンプルコードに戻ってこれらの問題を説明しましょう。
グローバルコンテキストを設定するとき、環境がスキャンされ、hoisting() 関数が識別子に付加されます。次に、次のステップで、変数 notyetdeclared が登録され、その値が unknown に初期化されます。コードの理解を続けるには、次の手順に従ってください。
それでは、サンプルコードで提起された 3 つの質問について説明しましょう:
6 行目、なぜこの関数が宣言される前にアクセスできるのでしょうか?
フェーズ 1 では、hoisting() 関数が識別子に登録されています。JS コードがフェーズ 2 のグローバル実行コンテキストで実行を開始すると、hoisting の字句環境が検索され、その定義の前に見つかります。関数。
1行目ではエラーが出ていませんが、この時点では変数notyetdeclaredが存在しないためでしょうか?
同様に、notyetdeclared はフェーズ 1 で識別子に登録され、未定義に初期化されるため、エラーはスローされません。
最後に、
4 行目の notyetdeclared がグローバル スコープで宣言されているのに、9 行目に出力されたときにまだ未定義なのはなぜですか?
今度は関数ホイスティング環境に入ります。フェーズ 1 では、notyetdeclared の変数がこの字句環境にまだ登録されていないため、notyetdeclared が登録され、未定義に初期化されます。 12 行目に var キーワードが含まれていない場合は、状況は異なります。
JavaScript におけるホイスティングは、技術的に言えば、関数や変数がどこにも移動しないということを説明するために使用する単なる視点であることが明らかになったことを願います。
概要
以上がJavaScriptでの巻き上げの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。