ホームページ  >  記事  >  ウェブフロントエンド  >  JS の実行コンテキスト、実行スタック、イベント ループを理解する

JS の実行コンテキスト、実行スタック、イベント ループを理解する

青灯夜游
青灯夜游転載
2020-07-06 10:39:171960ブラウズ

この記事では、JavaScript の実行コンテキスト、実行スタック、イベント ループについて説明します。一定の参考値があるので、困っている友達が参考になれば幸いです。

JS の実行コンテキスト、実行スタック、イベント ループを理解する

次の概念は、実行コンテキストであっても、実行スタックであっても、仕様では非常に抽象的です。想像に頼った内容が多いので、間違いがあればご指摘ください。

実行コンテキスト

要するに、実行コンテキスト (実行コンテキスト) は、実行可能コードが実行されている環境を抽象化したものです。コード ブロック内の変数の評価をトレースします。これは私がまとめた概念であり、多少不正確な部分があるかもしれませんが、実際の標準定義を参照することもできます。

ただし、一般に、重要な点が 3 つあります。

  • 実行可能コードのみが実行コンテキストを持ちます

  • 実行コンテキストステートフル: 実行状態 (Perform)、一時停止状態 (Suspend)、および再開 (Resume)。 Perfrom 状態の実行コンテキストは、実行中の実行コンテキスト (Running Execution Context) と呼ばれます。

  • 実行コンテキスト は、字句環境とまったく同等ではありません。関係性を言うのは難しいですが、前者が後者を引用しているだけです。

  • JS スクリプトを実行する場合、複数の実行コンテキストが存在する可能性がありますが、実行コンテキストは 1 つだけです (非同期の場合も同様です。なぜ 4 つ挙げられているかについては、 ..3 四大王がいるのは常識ではないでしょうか...)。

そして、ES 仕様では、実行可能コードが次のように規定されています:

  • グローバル コード

  • 関数コード

  • Eval文

  • モジュールコード

その他つまり、次のコードを見てください:

  
  var g=111
  function f(){
      
      console.log(g);
      for(let i =0; i  上記のコードを実行すると、2 つの実行コンテキストのみが生成されます: <p></p>
  • global

  • Function

    f

  • ##ただし、
(*)

とマークされた行をコメントアウトすると、実行コンテキストは 1 つだけになります。関数 f はまったく実行されず、当然、対応する実行コンテキストも存在しないためです。この中で唯一紛らわしいのは、これが for ループ であることですが、 これはまったく実行可能コードではないため、関数実行コンテキストの一部です

実行コンテキストの重要なコンポーネント

実行コンテキストは次のように抽象化できます:

ExecutionContext = {
    State: 
    LexEnv = {
        This: ,
        OuterEnv: ,
        DecRec:{
            //... identifiername-variable
        }      
    }
    VaEnv = {
        This: ,
        OuterEnv: ,
        VarRec:{
            //... identifiername-variable
        }      
    }
}

実際には、実行コンテキストには 2 つあります。非常に重要です。コンポーネント:

LexicalEnvironmentComponent

(字句環境コンポーネント) および VariableEnvironmentComponent (変数環境コンポーネント)。字句環境コンポーネントは 現在のコード字句環境 (LexEnv) を指し、変数環境コンポーネントは ## の 変数環境を指します。 #現在のコード (VarEnv)。 実行コンテキストについていくつか言わなければならないことがあります。非常に重要な部分の 1 つは

スコープ チェーン

ですが、実行コンテキスト##には関連するコンテンツはありません#で見られました。ただし、 スコープ チェーン は存在します。これは [[Scope]] 内部プロパティにあり、 ブラウザ を通じて直接確認できます。 しかし、このように理解することもできます。実行コンテキストが作成されると、現在の語彙環境の LexEnv だけでなく、LexEnv.OutEnv# も作成されます。 ##, ## が作成され、 #LexEnv.OutEnv.OutEnv

… 全世界に広がるまで。

実行コンテキストの作成と破棄1. 新しい実行コンテキスト (ExecutionContext、EC) を作成します。

2. 現在のレキシカル環境を作成します ( LexEnv および VarEnv)

3. 実行コンテキストの LexicalEnvironmentComponent

および

VariableEnvironmentComponent

を次の

LexEnv

および

VarEnv## にポイントします。現在の環境。#middle. 4. 新しい実行コンテキストを 実行スタックにプッシュし、ランタイム実行コンテキストになります。 5. 実行可能コード ブロック内の識別子をインスタンス化して初期化します:

現在の字句環境で宣言されたすべての識別子を収集します #Into DecRec

,

var

で宣言されたすべての識別子は、
    VarNames
  • コレクションに含まれています。この段階では、識別子が # で宣言されている場合、

    識別子名 が検出処理されます。 ##let/const/...VarNames の識別子と同じである場合、エラーが報告されます。 DecRec で識別子をインスタンス化し、uninitialized に設定します。 VarNames の識別子は

    ObjRec
  • にバインドされ、インスタンス化後に
  • unknown

    に直接初期化されます。

  • 对于function声明的函数,将直接指向函数对象,并也会绑定到ObjRec中,这是浏览器默认行为

6、运行代码。

  • 非var声明的标识符会在声明处进行初始化(默认为undefined)。

  • 完成所有变量的赋值,并可能会一直在变化。

7、运行完毕从 执行栈 中弹出。

备注:

  • 关于This绑定,大部分情况可以用过去的说法解释,然而某些情况下却不尽然。
  • 闭包我会在下一篇介绍。
  • 执行上下文,我个人认为并不如何重要,但是却能在许多情形下起到极为关键的作用,所以还是有必要去深入认识一下。
  • 关于执行上下文和词法环境的关系,最多是前者引用了后者,仅此而已。诚然,有许多情况没必要用执行上下文来说明,但是永远避免不了违和感

执行栈与事件循环

执行栈(Execution Stack)就是由执行上下文构成的堆栈,类似于Call Stack

1、当Javascript引擎遇到一段可执行代码时,新建一个执行上下文。

2、将它推入执行栈中。并设置为运行时执行上下文。

  • 如果存在其他执行上下文。
  • 那么将当前执行上下文挂起
  • 然后再将新执行上下文推入执行栈中。

3、执行上下文运行完毕,弹出销毁恢复并将原执行上下文设为运行时。

总觉得这些没什么好说的,但是水一下吧

执行栈最重要的部分并非是执行栈概念本身,而是与任务队列的关系,它是事件循环的入门关键概念之一

众所周知,Javascript语言是单线程的,此处的执行栈就相当于主线程的调用栈,也是唯一一个调用栈,至于什么是主线程可以查阅相关资料,这里有些超纲了……

那么javascript是如何实现异步的

确切来说,这不是Javascript核心的部分,它是结合浏览器API(如Web Worker, Browser-context了解一下)实现的

事件循环中(事件处理过程),有两个极其重要的概念:

  • 任务序列: Task Quenue
  • 事件: Event

这两个概念,是抽象滴。

在Javascript中,一个任务也可以称之为事件,通常是一个函数回调,由许多任务组成的队列,就是所谓的任务序列了。任务序列有很多分类,例如:作业序列(Job Quenue)、消息序列(Message Quenue),本质没区别。

不必再深入了解,现在需要记住的是:一个任务序列中的任务如果想要被执行,就必须将它取出放入执行栈中。

举一个抽象点的例子:

例如下面的代码:

  
      var temp = 10;
      
      console.log('push task1');
      setTimeout(function task1(){
        temp+=10;
        console.log(temp+'task1 okay! ');
      },1000)
      
      console.log('taskquenue=[task1]; push task2');
      setTimeout(function task2(){
        temp*=10;
        console.log(temp+'task2 okay! ');
      },500) 
     
      console.log('taskquenue=[task1,task2]; push task3');
      setTimeout(function task3(){
        temp*= -0.2;
        console.log(temp+'task3 okay! ');
      },1500)
      console.log('taskquenue=[task1, task2,task3]');

输出如下:

push task1
taskquenue=[task1]; push task2
taskquenue=[task1,task2]; push task3
taskquenue=[task1, task2,task3]
100task2 okay! 
110task1 okay! 
-22task3 okay!

setTimeout是一个定时器,它能够将任务放到任务队列中。如图:

  • 添加作业task1
    JS の実行コンテキスト、実行スタック、イベント ループを理解する
  • 添加作业task2
    JS の実行コンテキスト、実行スタック、イベント ループを理解する
  • 添加作业task3
    JS の実行コンテキスト、実行スタック、イベント ループを理解する

执行到此处, task1task2task3都被放入了任务队列; 然后执行栈全部执行完毕后,开始处理任务队列中的任务。

为什么任务队列中的任务必须在执行栈空时后执行呢?

  • 結局のところ、これは本当に根本的な内容に関係しているので、ここではわかりません。なぜこのようになるのかは理解できますが、他の人を誤解させるのが心配なので、忘れてください。
  • 一般的に、タスク シーケンス に関連する概念はそれほど単純ではなく、ブロッキングスケジュール(これらの側面については、C Java などの他のマルチスレッド言語を参照するか、オペレーティング システムの内容を確認することができます。
  • 単純な ビジネス実装 であれば、基礎となる多くのことを知る必要はありません。

ここでタスクの処理を開始します:

  • Processingtask2:
    JS の実行コンテキスト、実行スタック、イベント ループを理解する
  • Processingtask1
    JS の実行コンテキスト、実行スタック、イベント ループを理解する
  • 処理 task3
    JS の実行コンテキスト、実行スタック、イベント ループを理解する

わかりました、イベント ループ# # #それでおしまい。 その後、
Javascript エンジンは スリープ フェーズに入ります (Javascript エンジンは終了しません!)、新しいタスクが実行されるのを待ってから、次のイベントを開始しますループ。 備考:

    これは単なる単純な例です
  • イベント ループには複数のタスク キューを持つことができます
  • タスクシーケンスにはマイクロタスク シーケンスとマクロタスク シーケンスの 2 種類があります。
  • 今回のスクリプト コードはマクロタスク シーケンスの 1 つです。
  • 最後に: Javascript エンジン

これは私の

精読 Javascript シリーズです

3 番目の記事、不意を突かれた イベント ループ 、一度に多くのことを深く掘り下げるようです...しかし、これは最も 不合理なプログラミング配置だと思います。ほとんどのドキュメントでは、タスク シーケンスコール スタック#が分離されています。 ## ですが、~~_____~~ では、これらは 1 つである必要があり、都合のよい理由で分離してはなりません JavaScript の詳細については、仕様を読むだけでなく、JS エンジン の実装ドキュメントも読むことをお勧めします。一部の高度な内容は仕様には含まれていませんが、これらのドキュメントには含まれています。ドキュメント (Google で検索してください。Baidu でも検索できます)。

JavaScript エンジン に興味がある場合は、次を参照してください:

MDN About_JavascriptChromium V8 リファレンス

    Github V8
  • とはいえ、初心者が V8 のソースコードを一気に見るのはあまりお勧めできませんが、頭が割れるような感覚と全身が震える感覚はとても楽しいです。 ……
推奨関連チュートリアル:

JavaScript ビデオ チュートリアル

以上がJS の実行コンテキスト、実行スタック、イベント ループを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。