js 実行コンテキストとスコープの概要

js 実行コンテキストとスコープの概要

序文

有能なフロントエンド開発者であるか、または有能なフロントエンド開発者になりたいのであれば、JavaScript コードの実行プロセスを理解し、実行コンテキスト、スコープ、変数の昇格などの関連概念を理解し、それらを自分のコードに巧みに適用する必要があります。この記事は、「JavaScript You Don't Know」、「Advanced JavaScript Programming」、およびいくつかのブログを参照しています。

文章

1. JavaScriptコードの実行プロセスに関連する概念

js コードの実行は、コンパイラのコンパイルと js エンジンおよびスコープの実行の 2 つの段階に分かれています。コンパイラのコンパイル段階 (プリコンパイル段階) は、単語分割/字句解析、解析/構文解析、コード生成の 3 つの段階に分かれています。

(1)単語分割/字句解析段階では、コンパイラはコードを分割し、文を字句単位のストリーム/配列に分割する役割を担う。

(2)構文解析・字句解析フェーズでは、前フェーズの字句単位ストリームを、プログラムの文法構造に準拠したネストされた要素からなる抽象構文木に変換します。

(3)コード生成フェーズでは、抽象構文木が実行可能コードに変換され、JSエンジンに渡されます。

js コード実行の 3 つの重要な役割:

(1)jsエンジン:コード実行の全プロセスを担当

(2)コンパイラ:jsコードの構文を解析し、実行可能なコードを生成する

(3)範囲:宣言されたすべての識別子を収集して維持し、特定のルールに従って、宣言された識別子に対する現在のコードのアクセス権を決定します。

2. 実行コンテキストと実行スタック

JavaScript コードが実行されるときは常に、実行コンテキストで実行されます。実行コンテキストに関しては、実行スタックが何であるかを知っておく必要があります。他のプログラミング言語の「呼び出しスタック」である実行スタックは、コードの実行時に作成された実行コンテキストを格納するために使用される LIFO (後入れ先出し) データ構造を持つスタックです。 js エンジンは、実行するコードを初めて検出すると、まずグローバル実行コンテキストを作成し、それを現在の実行スタックにプッシュします。エンジンが関数呼び出しを検出するたびに、その関数の新しい実行コンテキストを作成し、それをスタックの先頭にプッシュします。js エンジンは、スタックの先頭にある関数を実行します。関数が実行されると、実行コンテキストがスタックからポップされ、制御フローが次のコンテキストに到達します。各実行コンテキストには、変数オブジェクト、スコープ チェーン、this という 3 つの重要なプロパティが含まれています。これらの特性も十分に理解する必要があります。

2.1 コンテキスト呼び出しスタック

var scope1 = "グローバルスコープ";
 関数 checkscope1(){
 var scope1 = "ローカルスコープ";
 関数f(){
 コンソールログ(スコープ1); 
 }
 f() を返します。
 }
 チェックスコープ1();
var scope2 = "グローバルスコープ";
 関数 checkscope2(){
 var scope2 = "ローカルスコープ";
 関数f(){
 コンソールログ(スコープ2);
 }
 f を返します。
 }
 チェックスコープ2()();

上記のコードスニペットは両方ともローカルスコープを出力します

上記のコードでは、スコープはローカル変数である必要があります。ブロックレベルのスコープを見つけることができます。f() がいつどこで実行されても、このバインディングは f() の実行時に有効です。同じ結果が表示されますが、2 つのコード セグメントの実行コンテキスト スタックの変更は異なります。

最初のコード: push(<checkscope1>functionContext)=>push(<f>functionContext)=>pop()=>pop()

2 番目のコード: push(<checkscope2>functionContext)=>pop()=>push(<f>functionContext)=>pop()

2.2 3種類の実行コンテキスト

(1)グローバルな文脈

js エンジンが js コードの解析を開始すると、最初に遭遇するのはグローバル コードです。初期化中に、グローバル実行コンテキストがコール スタックにプッシュされます。実行コンテキスト スタックは、アプリケーション全体が終了するとクリアされます。スタックの一番下は常にグローバル実行コンテキストです。これは、デフォルトまたは基本的なグローバル スコープです。関数内のコードはグローバル スコープ内にあります。最初にグローバル ウィンドウ オブジェクトが作成され、次にこの値がグローバル オブジェクトと同じに設定されます。プログラムには、グローバル実行コンテキストが 1 つだけ存在します。トップレベルの js コードでは、グローバル オブジェクトがドメイン チェーンの先頭であるため、これを使用してグローバル オブジェクトを参照できます。つまり、修飾されていないすべての変数と関数は、このオブジェクトの関数として照会されます。

つまり、グローバル実行コンテキストは 1 つだけであり、これは通常、クライアントのブラウザーによって作成されます。つまり、私たちがよく知っているウィンドウ オブジェクトであり、これを通じて直接アクセスできます。

(2)機能コンテキスト

関数が呼び出されるたびに、その関数の新しいコンテキストが作成されます。各関数には独自のコンテキストがあり、関数が呼び出されたときに作成されます。同じ関数が複数回呼び出されると、新しいコンテキストが作成されることに注意してください。

(3)コンテキスト付きで評価する

eval 内および関数を使用して実行されるコードにも独自の実行コンテキストがありますが、JavaScript 開発者は eval をあまり使用しないため、ここでは説明しません。

2.3 実行コンテキスト作成フェーズ

実行コンテキストの作成は、作成フェーズと実行フェーズの 2 つのフェーズに分かれています。

js エンジンは、実行コンテキスト作成フェーズで主に次の 3 つのことを行います。これを決定する ==> 語彙環境コンポーネントを作成する ==> 変数環境コンポーネントを作成する (まだよく理解していません)

(1)これを決定するが、詳細は説明しない。

(2)語彙環境コンポーネントを作成する

レキシカル環境は、ECMAScript コードのレキシカル ネスト構造に基づいて、識別子と特定の変数および関数との関連付けを定義する仕様タイプです。語彙環境は、環境レコーダーと、外部の語彙環境を参照する空の値(場合によっては)で構成されます。環境レコードは、現在の環境における変数や関数宣言の実際の位置を保存するために使用されます。外部環境導入レコードはわかりやすいです。自分の環境からアクセスできる他の外部環境を保存するために使用されます。こうなると、スコープチェーンの意味が少しあるのでしょうか。

語彙環境には 2 つの種類があります。

  • グローバル環境 (グローバル実行コンテキスト内) は、外部環境が参照されない語彙環境です。グローバル環境の外部環境参照が null です。組み込みのオブジェクト/配列など、環境レコーダー内のプロトタイプ関数 (ウィンドウ オブジェクトなどのグローバル オブジェクトに関連付けられているもの)、およびユーザー定義のグローバル変数があり、この値はグローバル オブジェクトを参照します。
  • 関数環境では、関数内のユーザー定義変数は環境レコーダーに保存されます。参照される外部環境は、グローバル環境、またはこの内部関数を含む任意の外部関数である可能性があります。

(3)可変環境コンポーネントの作成

変数環境は語彙環境とも言えます。語彙環境のすべての特性を持ち、環境記録と外部環境導入も持っています。 ES6 での唯一の違いは、レキシカル環境は関数宣言と let const で宣言された変数を格納するのに使用され、変数環境は var で宣言された変数のみを格納することです。

3. JavaScript スコープとスコープチェーン

3.1 範囲

レキシカルスコープは、コードの作成時または定義時に決定されますが、動的スコープは実行時に決定されます。(これも同じです) レキシカルスコープは関数が宣言される場所に焦点を当てますが、動的スコープは関数が呼び出される場所に焦点を当てます。 JavaScript はレキシカルスコープを使用しており、そのスコープは、コードの作成時に変数とブロックスコープを記述する場所によって決定されるため、字句解析器がコードを処理してもスコープは変更されません。スコープは、変数が漏洩したり公開されたりすることを防ぐ独立した領域であると理解できます。つまり、スコープの最大の用途は変数を分離することであり、異なるスコープ内の同じ名前の変数間で競合が発生することはありません。

範囲を理解する前に質問を見てみましょう

関数foo(){
 console.log(値);
 }
 var 値 = 1;
 関数バー() {
 var 値 = 2;
 console.log(値);
 関数 foo();
 }
 バー();

上記のコードの出力は何でしょうか? まず、グローバル コンテキストで foo() 関数、値変数 (値は未定義)、bar() 関数が宣言されています。コード実行フェーズでは、bar 関数コンテキストがスタックにプッシュされて実行され、値が 2 として出力されます。次に foo() が実行され、foo() がスタックにプッシュされます。値を出力する際に​​変数が見つからないため、js エンジンは上位スコープ、つまりグローバル スコープを検索し、1 を出力します。関数が実行されると、コンテキストはスタックからポップされます。次の関数を見てみましょう。スコープは階層化されています。内側のスコープは外側のスコープの変数にアクセスできますが、その逆はできません。

ES6 以降、js のスコープは、グローバル スコープ、関数スコープ、ブロック スコープ、およびデセプション スコープに分割されます。

3.1.1 グローバルスコープ

コード内のどこからでもアクセスできるオブジェクトにはグローバル スコープがあり、最も外側の関数と最も外側の関数の外側で定義された変数にはグローバル スコープがあり、未定義の変数と直接割り当てられた変数はすべて自動的にグローバル スコープを持つように宣言されます。

3.1.2 関数のスコープ

関数スコープとは、この関数に属するすべての変数が関数全体のスコープ内で使用および再利用できることを意味します (実際には、ネストされたスコープ内でも使用できます)。
この原則は、ソフトウェア設計において、必要なコンテンツは可能な限り最小限に公開し、他のコンテンツは「非表示」にする必要があることを意味します。
関数式は匿名にすることができますが、関数宣言では関数名を省略することはできません。
3.1.3. ブロックスコープ ブロックスコープは、通常、{ .. } 内の
(1)if、try/catchはブロックスコープを作成します。
(2)letキーワードは変数を任意のスコープ(通常は{ .. }内)にバインドできます。
(3)forループの先頭にあるletはiをforループブロックにバインドするだけでなく、実際にはループの各反復に再バインドし、前のループ反復の終了時の値が再割り当てされるようにします。
(4)constはブロックスコープ変数を作成するためにも使用できますが、その値は固定(定数)されます。オブジェクトの作成時に値を変更できます。
3.1.4. レキシカルスコープをごまかすメソッド、eval() と with()
eval() パラメータは文字列であり、その内容はその場所に記述されたコードとして扱われます (非厳密モード)。
オブジェクトの複数のプロパティを() で繰り返し参照する必要がある場合、オブジェクト自体を繰り返し参照する必要はありません。

3.2 スコープチェーン

スコープ チェーンは、基本的に、名前で変数 (識別子名) を検索するための一連のルールです。ルールは非常にシンプルです。変数が自身の変数オブジェクト内で見つからない場合は、親変数オブジェクト内を検索します。最も外側のグローバル コンテキストに到達すると、見つかったかどうかに関係なく検索プロセスは停止します。最初に一致する変数が見つかると検索は停止します。これをシャドウイングと呼びます。

スコープ チェーンの目的は、実行環境がアクセスできるすべての変数と関数への順序立ったアクセスを保証することです。
スコープ チェーン: 関数が定義されると、システムは関数のスコープ チェーンを格納する ([scope]) 属性を生成します。スコープ チェーンの 0 番目のビットには、現在の環境のグローバル実行コンテキスト GO が格納されます。GO には、関数やグローバル変数を含むすべてのグローバル オブジェクトが格納されます。関数が実行前にプリコンパイルされると、スコープ チェーンの最上部 (0 番目のビット) には関数によって生成された実行コンテキスト AO が格納され、最初のビットには GO が格納されます。
変数を検索するには、関数が格納されているスコープ チェーンの先頭から始めて、下方向に検索します (関数内のスコープは先頭にあるため、関数は外部変数にアクセスできますが、外部は関数内の変数にアクセスできないことがわかります)。

4. 実行コンテキストとスコープの違い

すべての関数呼び出しには、スコープとコンテキストが関連付けられています。基本的に、スコープは機能ベースであり、コンテキストはオブジェクトベースです。言い換えれば、スコープは関数が呼び出されるたびに変数がどのようにアクセスされるかに関するものであり、各呼び出しは独立しています。コンテキストは常にキーワード this の値であり、これは現在実行可能なコードを呼び出したオブジェクトへの参照です。スコープは関数が定義されたときに決定されます。関数内の変数は関数のスコープと関連しており、関数が実行されるスコープも関数が定義されたときのスコープと関連しています。コンテキストは主に、関数の実行時に決定されるキーワード this の値を指します。簡単に言えば、this はこの関数を呼び出す人を指します。

5. 最後

以上がjs実行コンテキストとスコープの詳細な概要です。js実行コンテキストとスコープの詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • JavaScript 上級プログラミング: 変数とスコープ
  • JavaScript スコープチェーンの基本原理のグラフィカルな説明
  • JavaScript の静的スコープと動的スコープを例を使って説明します
  • Javascript のスコープとクロージャの詳細
  • JS の難しさ 同期と非同期、スコープとクロージャ、プロトタイプとプロトタイプ チェーンの詳細な説明
  • Vue.js スロットにおけるスコープ付きスロットの使用法の詳細な説明
  • JS のスコープの問題に関する考えと共有

<<:  Mac でソースコードから MySQL 5.7.17 をコンパイルしてインストールするチュートリアル

>>:  Docker Swarm を使用して分散クローラー クラスターを構築する例

推薦する

Dockerイメージ解析ツールのダイブ原理解析

今日は、Docker イメージ、各レイヤーの内容を調べ、Docker/OCI イメージのサイズを縮小...

HTML テーブル マークアップ チュートリアル (28): セルの境界線の色属性 BORDERCOLOR

テーブルを美しくするために、セルごとに異なる境界線の色を設定できます。基本的な構文<TD 境界...

MySQLインスタンスが起動できない問題の分析と解決

目次序文シナリオ分析要約する序文数日前、友人がWeChatで私に連絡してきて、マシンがダウンタイムか...

HTML ウェブページ画像タグ

画像タグ <IMG> を挿入します。今日私たちが目にするカラフルなウェブページはすべて、...

Navicatを使用してクラウドサーバーデータベースにリモート接続する方法

秘密鍵を開かずにリモート サーバーのデータベースに接続するのは非常に便利です。新しい接続でデータを入...

MySQL COUNT関数の使用と最適化

目次COUNT 関数は何をするのですか? MyISAMの「魔法」シンプルなCOUNT最適化近似値を使...

ボタンをクリックした後のCSS読み込み効果を実現する

自社製品にクリック後1~2秒待機時間があるボタン(確認メールを送信する)があるため、クリック後の1~...

WeChatアプレットは写真の撮影とアルバムからの写真の選択を実現します

この記事では、WeChatアプレットで写真を撮ったり、アルバムから写真を選択したりするための具体的な...

画像の盗難を防ぐために Nginx で Referer を設定する方法

サーバーの画像が他のウェブサイトからホットリンクされると、サーバーの帯域幅とアクセス速度に影響します...

CocosCreator 入門チュートリアル: TS で初めてのゲームを作る

目次前提TypeScript と JavaScriptコードエディタの選択TypeScriptを学ぶ...

VUEプロジェクトでXSS攻撃に遭遇した実体験

目次序文原因を発見するカスタムフィルタリングルール要約する序文インターネットの急速な発展に伴い、情報...

純粋な CSS でフォ​​ーム検証を実装するためのサンプル コード

日常業務において、フォームの検証は非常に一般的な設計要件です。ログイン ボックスや登録ボックス、アン...

Vue.jsの機能コンポーネントに関する包括的な理解

目次序文React 関数コンポーネントVue (2.x) の機能コンポーネント🌰 例: el-tab...

Navicatを使ってMySQLを操作する方法

目次序文: 1. Navicatの紹介2. シンプルなチュートリアルの共有接続管理ライブラリテーブル...

Vueプロジェクトの支払い機能コードの詳細な説明

1. Alipay方式: Alipay メソッド: Alipay をクリックして支払い、バックエンド...