「JS篇」JavaScript 執行上下文和提高

咱們一般將 JavaScript 歸類爲動態或解釋執行語言,但實際上它也是一門編譯語言,它有本身的編譯器形式,運行在 JavaScript 引擎中。web

每一個 Web 瀏覽器都有本身的 JavaScript 引擎形式:Chrome 有 V8,Mozilla 有 SpiderMonkey 等。這些 JavaScript 引擎的共同點都是將 JavaScript 代碼轉換爲編譯器能夠理解的語言,而後執行它。瀏覽器

執行上下文 Execution Context

當 JavaScript 代碼運行的時候,運行 JavaScript 代碼的環境造成了執行上下文 ,執行上下文決定代碼能夠訪問哪些變量、函數、對象等。微信

咱們將執行上下文簡單視爲運行當前代碼的 environment / scope,咱們知道做用域分爲 global scopelocal scopeapp

相似的,執行上下文也分爲不一樣的類型:ide

全局執行上下文 - 代碼首次執行時候的默認環境,在代碼的整個執行過程當中,只用一個全局執行上下文。函數

函數執行上下文 - 每當執行流程進入到一個函數體內部的時候,就會建立一個函數執行上下文,能夠有任意數量的函數執行上下文。this

clipboard.png

執行棧/調用棧

JavaScript 是單線程的,瀏覽器只分配給 JavaScript 一個主線程,一次只能執行一個任務(函數),所以它在執行棧中對其餘操做(事件和函數執行)造成一個任務隊列,排隊等候執行。spa

clipboard.png

每當在瀏覽器中加載腳本時,棧 stack 中的第一個元素就是全局執行上下文。當有函數執行時,將建立一個函數執行上下文,並將其置於全局執行上下文之上。一旦函數執行完成,它就會從執行堆棧中彈出,並將控制權交給它下面的上下文中。結合上面說到的,咱們看一個例子:線程

var name = "global variable";
console.log(name)

function func1() {
  console.log("func1 被調用了。")
  func2();
}
function func2() {
  console.log("func2 被調用了。");
}
func1();

clipboard.png

當上述代碼在瀏覽器中加載時:指針

  • Javascript 引擎建立一個全局執行上下文 global execution context 並將其推送到當前執行棧。
  • 接着進行 func1() 被調用,而後 Javascript 引擎爲該函數建立一個新的函數執行上下文 function execution context 並將其推送到全局執行上下文的頂部。
  • 在執行 func1() 過程當中,發現 func2() 被調用,Javascript 引擎爲該函數建立一個新的執行上下文,並將其推送到 func1() 執行上下文的頂部。
  • func2() 函數完成時,其執行上下文從當前堆棧彈出,將控制權交給其下面的執行上下文,即 func1() 函數執行上下文。
  • func1() 完成後,其執行堆棧將從堆棧中刪除,將控制權交給全局執行上下文。一旦執行了全部代碼,JavaScript 引擎就會從當前堆棧中刪除全局執行上下文。

執行上下文階段

執行上下文主要有兩個階段:建立階段執行階段,接下來咱們將逐一進行介紹。

建立階段

在函數執行發生以前,JavaScript 引擎會作以下事情:

  • 首先,爲每一個函數或變量建立與外部環境的鏈接,這些函數造成做用域鏈 scope chain。做用鏈告訴執行上下文它應該包含什麼,以及它應該在哪裏查找解析函數的引用和變量的值。(對於全局環境,外部環境爲 null。在全局做用域內的全部環境都將把全局環境做爲其外部環境)。
  • 掃描做用鏈後,將建立一個環境存儲器,其中全局上下文的建立和引用(Web瀏覽器中的窗口),變量、函數和函數參數的建立和引用在內存中完成。
  • 最後,在第一步中建立的每一個執行上下文中肯定 this 關鍵字的值(對於全局執行上下文,this 指向 window)。

咱們能夠將建立階段使用僞代碼這樣表示:

creationPhase = { // 建立階段
  'outerEnvironmentConnection': { // 建立外部鏈接
        // 造成做用域鏈
   },    
   'variableObjectMapping': {
        // 變量、函數和函數參數的建立和引用在內存中完成。
   },
   'valueOfThis': {},  // 肯定 this 的值
}

執行階段

這是代碼在建立階段造成的執行上下文中的運行的階段,而且逐行分配變量值。

當執行開始時,JavaScript 引擎在其建立階段對象中查找執行函數的引用。若是在當前對象中沒有找到,它將沿着做用域鏈繼續向上查找,直到它到達全局環境。

若是在全局環境中找不到函數引用,則將返回錯誤。若是找到了引用而且函數正確執行,那麼這個特定函數的執行上下文將從棧中彈出,接着 JavaScript 引擎將移動到下一個函數,它們的函數執行上下文將被加入到棧中並執行,以此類推。

讓咱們經過示例來看看上面的兩個階段,以便更好地理解它。

let name = "webinfoq";
var title = "execution context";
const message = "hello world";

function func1(num) {
  var author = "deepak";
  let value = 3;
  let func2 = function multiply() {
    return num * value;
  }
  const fixed = "Divine";
  function addFive() {
    return num + 5;
  }
}
func1(10);

所以,全局執行上下文將以下表示:

globalExecutionObj = {  // 全局執行s上下文
    outerEnvironmentConnection: null,  // 全局上下文外部環境爲 null
    variableObjectMapping: { 
        name: uninitialized,  // 在建立階段,let聲明的變量是未初始化狀態
        title: undefined,   // var 聲明的變量表示爲未定義
        date: uninitialized, // 在建立階段,const聲明的變量是未初始化狀態
        func1: <func1 reference>,  func1 地址引用
    },
    this: window //Global Object  
}
注意: letconst 定義的變量在建立階段沒有任何與之關聯的值,但 var 定義的變量在建立階段爲 undefined
這就是爲何能夠在 var 聲明以前訪問變量,(獲得的是 undefined), 在 letconst
聲明以前訪問會報錯(暫時性死區)。

這就是所謂的變量提高,全部使用 var 聲明的變量都會被提高到做用域的頂部。

執行階段,完成對變量的賦值等操做。所以,在執行階段,全局執行上下文global execution 看起來像這樣:

globalExectutionObj = {  // 全局執行上下文
    outerEnvironmentConnection: null,
    variableObjectMapping: {
        name: "webinfoq",
        title: "execution context",
        message: "hello world",
        func1: pointer to function func1, // 指向func1的指針
    },
    this: window //Global Object
}

當執行到 func1() 時,將造成新的函數執行上下文 function execution global,建立階段以下所示:

func1ExecutionObj = {  // func1 函數執行上下文
    outerEnvironmentConnection: Global,  // 外部環境爲全局環境
    variableObjectMapping: {
       arguments: {
            0: 10,
            length: 1
        },
        num: 10,
        author: undefined,  // var 聲明的
        value: uninitialized,  // let 聲明的
        func2: uninitialized,  // let 聲明的
        fixed: uninitialized,  // const 聲明
        addFive: pointer to function addFive()  // 指向函數addFive的指針
    },
    this: Global Object or undefined  
}

執行階段:

func1ExecutionObj = {
    outerEnvironmentConnection: Global,  
    variableObjectMapping: {
       arguments: {  // 先處理 arguments 參數
            0: 10,
            length: 1
        },
        num: 10,
        author: "deepak",  //變量f賦值
        val: 3,
        func2: pointer to function func2() 
        fixed: "Divine"
        addFive: pointer to function addFive()
    },
    this: Global Object or undefined
}

Javascript 引擎建立執行上下文調用棧。當有函數執行時,引擎就會建立一個新的函數執行上下文。最後所用函數執行完成後,將更新全局環境,而後全局代碼完成,程序結束。

瞭解更多請關注微信公衆號:webinfoq

clipboard.png

相關文章
相關標籤/搜索