#JavaScriptの実行コンテキストとキュー(スタック)の関係?

6339 ワード

  • 原文:What is the Execution Context&Stack in JavaScript?
  • gitアドレス:JavaScriptでの実行コンテキストとキュー(スタック)の関係?
  • ガイド:以前は、どのような変数が上がるのか、関数が上がるのか、どのような関数が優先度が変数より大きいのか、いつも知っていたので、面接官が同じnameを持っているのに、functionを続け、varと値を割り当てているのを見ていました.どんな変数の昇格と関数の昇格を持って説明するのは通じません.少なくとも私は--.David Shariffのこの記事ではその原理を説明してくれていて、おおらかに見られました
  • この文章では、JavaScriptの最も基本的な部分を深く検討し、コンテキストを実行します.本明細書の終了時に、解釈器が何をしたのかをより明確にし、一部の関数、変数が宣言される前に使用され、値がどのように決定されるかをより明確にします.
    実行コンテキストとは?
    JavaScriptでコードが実行される場合、その実行環境は非常に重要であり、以下のクラスに分けられます.
  • globalコード--コードを初めて実行するデフォルト環境
  • functionコード--実行フローが関数体に入るたびに
  • Evalコード--内部eval関数内で実行するテキスト
  • 理解を容易にするために、本明細書で実行コンテキストとは、現在実行されているコードの環境、役割ドメインを指す.次に、global、function contentを含む実行コンテキストのコードを見てみましょう.
    ここでは特に何もありませんが、1つのglobal contextは紫色の枠線で表され、3つの異なるfunction contextsはそれぞれ緑、青、オレンジ色の枠線で表されます.プログラム内の他のコンテキストからアクセスできるglobal contextは1つしかありません.
    任意の数のfunction contextsを持つことができ、各関数呼び出しによって新しいコンテキストが作成され、現在の関数の役割ドメイン外から関数内部宣言の内容に直接アクセスできないプライベート役割ドメインが作成されます.上記の例では、関数は現在のコンテキストの外で宣言された変数にアクセスできますが、外部コンテキストは内部で宣言された変数/関数にアクセスできません.どうしてこんなことになったの?このコードはいったいどのように実行されていますか?
    コンテキストスタックの実行
    ブラウザのJavaScriptインタプリタの単一スレッドが実行されます.これは、ブラウザが同じ時間に1つのことしか実行しないことを意味し、他のイベントが実行キューに並んでいることを意味します.次の図は、単一スレッドキューの抽象ビューです.
    ブラウザがスクリプトを最初にロードすると、デフォルトでグローバル実行コンテキスト(global execution content)に入ります.グローバルコードで関数を呼び出すと、プログラムのシーケンスストリームが呼び出された関数に入り、新しい関数execution contextを作成し、コンテキストを上部execution stack(実行キュー)にプッシュします.
    現在の関数で別の関数を呼び出すと、同じことが起こります.コードの実行プロセスは内部関数に入り、execution contextを作成し、実行キューの上部にプッシュします.ブラウザは常にスタックの上部にあるexecution contextを実行し、関数が現在の操作execution contextの実行を完了すると、スタックの上部からポップアップされ、現在のスタックの下のコンテキストに制御権が戻ります.次の例では、再帰関数とプログラムexecution stackを示します.
    (function foo(i) {
        if (i === 3) {
            return;
        }
        else {
            foo(++i);
        }
    }(0));

    コードは自身を3回だけ呼び出し、iの値を1ずつインクリメントする.foo関数を呼び出すたびに、新しい実行コンテキストが作成されます.実行が完了すると、スタックがポップアップされ、global context(koa 2のタマネギ図は思いついたか?)に再び到達するまで下のコンテキストに制御権が渡されます.
    以下に、実行キューの5つのキーを示します.
  • シングルスレッド・
  • 同期実行
  • グローバルコンテキスト
  • 無限レベルの関数コンテキスト
  • 各関数呼び出しは、自身への呼び出し(再帰)を含む新しい実行コンテキスト(execution context)を作成する
  • 実行コンテキストの詳細
    したがって、関数を呼び出すたびに新しい実行コンテキスト(execution context)が作成されることがわかりました.ただし、JavaScriptインタプリタでは、生成実行コンテキスト(execution context)を呼び出すたびに2つのフェーズがあります.
  • 作成フェーズ[関数を呼び出す場合、任意のコードを実行する前に]:
  • 役割ドメインチェーンを作成する.
  • 変数(variables)、関数(functions)、パラメータ(arguments)の作成
  • 「this」を確定する.
  • アクティブ/実行フェーズ:
  • var賦課、(function宣言)指向関数、解釈/実行コード
  • 各execution contextは、概念的に3つの属性を持つオブジェクトとして表すことができます.
    executionContextObj = {
        'scopeChain': { /* variableObject + all parent execution context's variableObject */ },
        'variableObject': { /* function arguments / parameters, inner variable and function declarations */ },
        'this': {}
    }

    アクティブ/変数オブジェクト[AO/VO]
    このexecutionContextObjは、関数を呼び出すときに作成されますが、実際の関数を実行する前に作成されます.これが最初のフェーズです.フェーズを作成します.ここで、インタプリタは、入力されたパラメータまたはarguments、ローカル関数宣言、およびローカル変数宣言をスキャンすることによってexecutionContextObjを作成する.今回のスキャンの結果はexecutionContextObjになりました.variableObject.
    次は、解釈器がコードの偽の解析方法の概要です.
  • 関数呼び出しに遭遇した.
  • functionコードを実行する前に、実行コンテキスト(execution context)を作成する.
  • 作成段階に入る:
  • アクティブドメインチェーンの初期化(Scope Chain).
  • 変数オブジェクトの作成(variable object):
  • arguments objectを作成し、パラメータのコンテキストを確認し、名前と値を初期化し、参照コピーを作成する.
  • コンテキストをスキャンして関数宣言を取得する:
  • 見つかった各関数について、variable objectに関数名を属性とするキー値ペアを作成し、値がメモリ内の関数の参照ポインタを指す.
  • 関数名が既に存在する場合、参照ポインタ値が上書きされます.

  • コンテキストをスキャンして変数宣言を取得する:
  • 見つかった変数宣言ごとにvariable objectに変数名の属性となるキー値ペアを作成し、値をundefinedに初期化する.
  • 変数名が既にvariable objectに存在する場合は、操作を一切行わずスキャンを継続する.


  • コンテキスト内の「this」の値を決定します.

  • アクティブ/実行フェーズ:
  • コンテキストで関数体のコードを実行/解析し、コードが行単位で実行されたときに変数に値を付与する.


  • 例を見てみましょう
    function foo(i) {
        var a = 'hello';
        var b = function privateB() {
    
        };
        function c() {
    
        }
    }
    
    foo(22);

    呼び出し時foo(22)、creation stage長は次のようになります.
    fooExecutionContext = {
        scopeChain: { ... },
        variableObject: {
            arguments: {
                0: 22,
                length: 1
            },
            i: 22,
            c: pointer to function c()
            a: undefined,
            b: undefined
        },
        this: { ... }
    }

    ご覧のように、creation stageは属性のnameを定義し、値を割り当てませんが、formal arguments/parameters(関数パラメータ、arguments)を除きます.creation stageが完了すると、実行プロセスは関数体に入り、関数が完了した後のexecution stageは次のようになります.
    fooExecutionContext = {
        scopeChain: { ... },
        variableObject: {
            arguments: {
                0: 22,
                length: 1
            },
            i: 22,
            c: pointer to function c()
            a: 'hello',
            b: pointer to function privateB()
        },
        this: { ... }
    }

    アップグレード
    多くのJavaScriptの資料では、解釈変数と関数宣言がその役割ドメインの上部に昇格した.しかし、なぜこのようなことが起こったのかを詳しく説明する人はいません.解釈器がactivation objectをどのように作成するかを把握すると、理解しやすくなります.例:
    (function() {
    
        console.log(typeof foo); // function pointer
        console.log(typeof bar); // undefined
    
        var foo = 'hello',
            bar = function() {
                return 'world';
            };
    
        function foo() {
            return 'hello';
        }
    
    }());​

    私たちが今答えられる質問は、
  • なぜ私たちはそれを宣言する前にfooにアクセスできるのですか?
  • creation stageに従うと、activation/code execution stageの前に変数が作成されていることがわかります.したがって、機能フローが実行されると、fooはactivation objectで定義されています.

  • fooは2回宣言したのに、なぜfooがfunctionなのか、_いいえ_义齿
  • fooが2回宣言しても、creation stage関数が変数の前にactivation objectbefore上に作成されていることを知っています.属性名がactivation objectにすでに存在する場合、解釈器は今回の宣言を無視します.
  • そのため、まずactivation objectにfoo()の参照が作成され、解釈器が到着するとvar foo、属性名fooが存在するため、コードは何もせず、続いていく.

  • なぜbarがundefinedなのか?
  • barは実際には関数付与値を持つ変数であり、creation stageで作成された変数であることが知られているが、初期値はundefinedである.


  • 概要
    JavaScriptインタプリタがコードをどのように実行するかを今までよく把握しておきたい.実行コンテキストとキューを理解すると、コードが予想されなかった理由がわかります.
    インタプリタの内部動作原理を理解することはJavaScriptの知識の重要な構成部分だと思いますか?実行コンテキストの各フェーズがJavaScriptの作成に役立つかどうか知っていますか?
    __注意_::閉パッケージ、コールバック、タイムアウトなどについて質問している人もいますが、次の記事では、役割ドメインチェーンとexecution contextの関係を主に概説します.
    広がる
  • ECMA-262-3 in detail. Chapter 2. Variable object
  • Identifier Resolution, Execution Contexts and scope chains