欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

了解JS中的執(zhí)行上下文、執(zhí)行棧、事件循環(huán)

本篇文章帶大家了解一下Javascript中的執(zhí)行上下文和執(zhí)行棧、事件循環(huán)。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

橫山網(wǎng)站建設公司創(chuàng)新互聯(lián)建站,橫山網(wǎng)站設計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為橫山超過千家提供企業(yè)網(wǎng)站建設服務。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設要多少錢,請找那個售后服務好的橫山做網(wǎng)站的公司定做!

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

執(zhí)行上下文

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

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

只有可執(zhí)行代碼才會有執(zhí)行上下文

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

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

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

并且ES規(guī)范中規(guī)定,可執(zhí)行代碼有下面幾個:

全局代碼

函數(shù)代碼

Eval語句

模塊代碼

換言之,看以下代碼:

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

如果上面的代碼運行,只會產(chǎn)生兩個執(zhí)行上下文:

全局

函數(shù)f

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

執(zhí)行上下文的重要組成部分

一個執(zhí)行上下文,可以抽象為:

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

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

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

不過也可以這樣理解,在一個執(zhí)行上下文被創(chuàng)建時,不僅會創(chuàng)建當前詞法環(huán)境的LexEnv,也會創(chuàng)建LexEnv.OutEnv、LexEnv.OutEnv.OutEnv…,直至延伸到全局為止。

執(zhí)行上下文的創(chuàng)建與銷毀

1、創(chuàng)建一個新執(zhí)行上下文(ExecutionContext , EC)

2、創(chuàng)建當前詞法環(huán)境(LexEnv 和 VarEnv )

3、將該執(zhí)行上下文的LexicalEnvironmentComponentVariableEnvironmentComponent指向當前環(huán)境下的LexEnvVarEnv中。

4、將新執(zhí)行上下文推入執(zhí)行棧中,并成為運行時執(zhí)行上下文。

5、對可執(zhí)行代碼塊內(nèi)的標識符進行實例化和初始化:

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

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

對于function聲明的函數(shù),將直接指向函數(shù)對象,并也會綁定到ObjRec中,這是瀏覽器默認行為。

6、運行代碼。

非var聲明的標識符會在聲明處進行初始化(默認為undefined)。

完成所有變量的賦值,并可能會一直在變化。

7、運行完畢從執(zhí)行棧中彈出。

備注:

關于This綁定,大部分情況可以用過去的說法解釋,然而某些情況下卻不盡然。閉包我會在下一篇介紹。執(zhí)行上下文,我個人認為并不如何重要,但是卻能在許多情形下起到極為關鍵的作用,所以還是有必要去深入認識一下。關于執(zhí)行上下文和詞法環(huán)境的關系,最多是前者引用了后者,僅此而已。誠然,有許多情況沒必要用執(zhí)行上下文來說明,但是永遠避免不了違和感。

執(zhí)行棧與事件循環(huán)

執(zhí)行棧(Execution Stack)就是由執(zhí)行上下文構(gòu)成的堆棧,類似于Call Stack。

1、當Javascript引擎遇到一段可執(zhí)行代碼時,新建一個執(zhí)行上下文。

2、將它推入執(zhí)行棧中。并設置為運行時執(zhí)行上下文。

如果存在其他執(zhí)行上下文。那么將當前執(zhí)行上下文掛起然后再將新執(zhí)行上下文推入執(zhí)行棧中。

3、執(zhí)行上下文運行完畢,彈出銷毀恢復并將原執(zhí)行上下文設為運行時。

總覺得這些沒什么好說的,但是水一下吧

執(zhí)行棧最重要的部分并非是執(zhí)行棧概念本身,而是與任務隊列的關系,它是事件循環(huán)的入門關鍵概念之一。

眾所周知,Javascript語言是單線程的,此處的執(zhí)行棧就相當于主線程的調(diào)用棧,也是唯一一個調(diào)用棧,至于什么是主線程可以查閱相關資料,這里有些超綱了……

那么javascript是如何實現(xiàn)異步的?

確切來說,這不是Javascript核心的部分,它是結(jié)合瀏覽器API(如Web Worker, Browser-context了解一下)實現(xiàn)的。

在事件循環(huán)中(事件處理過程),有兩個極其重要的概念:

任務序列: Task Quenue事件: Event

這兩個概念,是抽象滴。

在Javascript中,一個任務也可以稱之為事件,通常是一個函數(shù)回調(diào),由許多任務組成的隊列,就是所謂的任務序列了。任務序列有很多分類,例如:作業(yè)序列(Job Quenue)、消息序列(Message Quenue),本質(zhì)沒區(qū)別。

不必再深入了解,現(xiàn)在需要記住的是:一個任務序列中的任務如果想要被執(zhí)行,就必須將它取出放入執(zhí)行棧中。

舉一個抽象點的例子:

例如下面的代碼:

      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是一個定時器,它能夠?qū)⑷蝿辗诺饺蝿贞犃兄?。如圖:

添加作業(yè)task1
添加作業(yè)task2
添加作業(yè)task3

執(zhí)行到此處,task1、task2task3都被放入了任務隊列; 然后執(zhí)行棧全部執(zhí)行完畢后,開始處理任務隊列中的任務。

為什么任務隊列中的任務必須在執(zhí)行??諘r后執(zhí)行呢?

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

現(xiàn)在開始處理任務吧:

處理task2
處理task1
處理task3

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

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

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

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

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

MDN About_JavascriptChromium V8參考Github V8

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

相關教程推薦:JavaScript視頻教程

分享文章:了解JS中的執(zhí)行上下文、執(zhí)行棧、事件循環(huán)
瀏覽路徑:http://chinadenli.net/article28/cpdscp.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供面包屑導航、靜態(tài)網(wǎng)站、網(wǎng)頁設計公司、網(wǎng)站設計、響應式網(wǎng)站、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)