中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

什么是JS中的執行上下文、 執行棧、事件循環

發布時間:2020-07-29 09:29:50 來源:億速云 閱讀:293 作者:Leah 欄目:web開發

本篇文章給大家分享的是有關什么是JS中的執行上下文、 執行棧、事件循環,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

下面的這些概念,無論是執行上下文、 還是執行棧,它在規范中的概念都很抽象,很多內容的理解實際靠的都是想象力,若有錯誤之處,還請指正。

執行上下文

簡而言之,執行上下文(Execution Context)是正在運行的可執行代碼所處環境的抽象,用于追蹤一段代碼中變量的求值。這是我總結過來的概念,可能有些不準確,也可以參考真正的規范定義。

不過總的來說,有三個關鍵點:

  • 只有可執行代碼才會有執行上下文

  • 執行上下文是有狀態的:運行狀態(Perform)、掛起狀態(Suspend)以及恢復(Resume)。處于Perfrom狀態的執行上下文稱之為運行時執行上下文(Running Execution Context)

  • 執行上下文完全不等價于詞法環境,硬說關系,也只是前者引用了后者而已。

  • 執行一個JS腳本時,可以有多個執行上下文存在,但是 運行時上下文只有唯一一個(異步也是如此,至于為什么提了四個……三大天王有四個不是常識么……)。

并且ES規范中規定,可執行代碼有下面幾個:

  • 全局代碼

  • 函數代碼

  • Eval語句

  • 模塊代碼

換言之,看以下代碼:

  
  var g=111
  function f(){
      
      console.log(g);
      for(let i =0; i < 10; i++){
          console.log(i);
      }
  }
  f();  /// (*)

如果上面的代碼運行,只會產生兩個執行上下文:

  • 全局

  • 函數f

但是如果將標注(*)的行注釋掉,那么最終只有一個執行上下文,因為函數f根本就不會執行,自然就不會有相應的執行上下文了。里面唯一一個迷惑的是,就是for-loop了,但它根本不是可執行代碼,所以它是函數執行上下文的一部分

執行上下文的重要組成部分

一個執行上下文,可以抽象為:

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

事實上,在一個執行上下文中有兩個相當重要的組件:LexicalEnvironmentComponent(詞法環境組件)和VariableEnvironmentComponent(變量環境組件)。詞法環境組件指向當前代碼中的詞法環境LexEnv), 變量環境組件指向當前代碼變量環境VarEnv)。

關于執行上下文不得不說的二三事中,有一個很重要的部分就是作用域鏈,但是在執行上下文中并沒有看到相關內容。不過作用域鏈的確存在,它就在[[Scope]]內部屬性中,通過瀏覽器可以直接看到。

不過也可以這樣理解,在一個執行上下文被創建時,不僅會創建當前詞法環境的LexEnv,也會創建LexEnv.OutEnvLexEnv.OutEnv.OutEnv…,直至延伸到全局為止。

執行上下文的創建與銷毀

1、創建一個新執行上下文(ExecutionContext , EC)

2、創建當前詞法環境(LexEnv 和 VarEnv )

3、將該執行上下文的 LexicalEnvironmentComponentVariableEnvironmentComponent 指向當前環境下的 LexEnvVarEnv中。

4、將新執行上下文推入 執行棧中,并成為運行時執行上下文

5、對可執行代碼塊內的標識符進行實例化和初始化:

  • 收集當前詞法環境內所有聲明的標識符歸入DecRec中,所有var聲明的標識符歸入VarNames集合中,在此階段會進行標識符名檢測,若與let/const/...聲明的標識符與VarNames中的標識符重復, 報錯。

  • DecRec中的標識符進行實例化,并設為uninitializedVarNames中的標識符綁定在ObjRec中,實例化后直接初始化為undefined

  • 對于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都被放入了任務隊列; 然后執行棧全部執行完畢后,開始處理任務隊列中的任務。

為什么任務隊列中的任務必須在執行棧空時后執行呢?

  • 這里我也不清楚,這畢竟真正涉及了底層內容;只是有些明白為何這樣,不過擔心誤導他人,就算了。
  • 一般而言, 關于任務序列的相關概念沒有如此簡單,還涉及了很多東西,例如阻塞調度(Schedule)等,這些方面可以參考其他諸如C++Java這類多線程的語言或是看看操作系統這方面的內容。
  • 如果只是簡單的業務實現,不需要了解這么多底層的東西。

現在開始處理任務吧:

  • 處理task2
    什么是JS中的執行上下文、 執行棧、事件循環
  • 處理task1
    什么是JS中的執行上下文、 執行棧、事件循環
  • 處理task3
    什么是JS中的執行上下文、 執行棧、事件循環

好了,一個事件循環就這么結束了。
然后Javascript引擎進入休眠階段Javascript引擎永不結束!),等待有新的任務執行,然后開始下一個事件循環。

備注:
  • 這里只是一個簡單例子
  • 事件循環可以有多個任務隊列
  • 任務序列共分為兩種: 微任務序列和宏任務序列
  • 我們的script代碼,就是宏任務序列之一。

最后: Javascript引擎

這是我精讀Javascript系列第三篇,猝不及防的就到了事件循環,看起來一下子就深入好多好多…… 但是我覺得這才是最合理程安排,大多的文檔都把任務序列調用棧給分開了,但是在~~_____~~,它們本應該就是一體,不應該以任何方便的理由將它們分開

深入Javascript, 除了看規范,最好也看下JS引擎的實現文檔,有些進階內容,不在規范中,而是在這些文檔里(請自己Google查找,百度也能搜索到)。

如果對Javascript引擎比較感興趣的,可以參考:

  • MDN About_Javascript
  • Chromium V8參考
  • Github V8

雖然,極其不建議新手一下子看V8源代碼,那種頭痛欲裂又渾身顫抖不止的感覺實在是過癮之極啊……

以上就是什么是JS中的執行上下文、 執行棧、事件循環,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

旬阳县| 奈曼旗| 滕州市| 日喀则市| 杂多县| 鹿泉市| 正宁县| 保靖县| 五大连池市| 玉环县| 元谋县| 微山县| 崇礼县| 安达市| 荆州市| 屏南县| 炎陵县| 门头沟区| 巫溪县| 禄丰县| 隆德县| 陇南市| 平遥县| 区。| 祁阳县| 乌海市| 蒲江县| 利川市| 平遥县| 商南县| 五大连池市| 宁陕县| 菏泽市| 开封县| 承德市| 水富县| 小金县| 南漳县| 兰考县| 榕江县| 历史|