前端筆記--JS執行上下文

寫在前面:前段時間參加了金山的前端面試,面試過程當中常常get不到面試官的點,有時候即便理解了面試官的意思,卻又不能準確的表達出來。讓我意識到當前最大的問題是表達溝通能力不足。因此指望經過撰寫學習文檔的方式,來提高本身的表達和總結的能力。前端

執行上下文

基本概念

執行上下文是指當前Javascript代碼被解析和執行時所在環境的抽象概念,JavaScript 任何代碼的運行都是在執行上下文中。面試

執行棧

js代碼運行在執行棧中,執行棧也叫作調用棧,具備後進先出的棧結構,用於存儲js代碼在執行過程當中建立的上下文環境。數組

<script>
    baz();
    foo();
    function baz() {
        console.log('in baz, we canot get bar: ', bar);
    }
</script>
<script>
    function foo() {
        console.log('in foo, wen can get bar: ', bar);
    }
    
    foo();
    var bar = 1;
    baz();
</script>
複製代碼

調用棧
以上面js文件執行爲例,來簡單闡述調用棧在函數執行過程當中的狀態。

  1. 在js引擎讀取到頁面js代碼時,建立全局執行上下文,放到執行棧中。從第一段js文本中能夠看出,此時全局對象中只包含baz這個函數變量。
  2. 建立徹底局執行上下文以後,開始執行這段js代碼,碰到baz(),將建立baz函數執行上下文。在函數執行過程當中,咱們嘗試訪問變量bar,可是在整個做用域鏈(下文解析做用域鏈概念)中都沒有這個變量的定義。因此這時候會拋出referenceError錯誤。Uncaught ReferenceError: bar is not defined。而後終止函數執行,將baz執行上下文移除。
  3. 因爲碰到異常,第一段js代碼提早結束。
  4. 此時,繼續解析第二段js代碼,因爲全局上下文已經被建立,則繼續向這個上下文中添加環境變量。包括bar = undefined; foo: fun
  5. foo()執行foo函數,建立foo函數執行上下文,並執行,因爲這時候bar並未被賦值,因此輸出的bar值爲undefined。執行完畢,移除foo上下文。
  6. 繼續執行bar = 1賦值操做,而後執行函數baz。將建立baz執行上下文,由於只存在一個全局執行上下文,因此此時baz的外部環境對象的引用包括已經賦值的bar變量,因此會輸出1。執行完畢,移除baz上下文。
  7. 當頁面銷燬時,移除全局執行上下文。

上下文分類

  1. 全局執行上下文 js引擎首次讀取頁面js文件時,會建立一個全局的執行上下文並推入執行棧。任何定義在全局中的變量都存在在這個上下文中。函數的運行也是基於這個全局執行上下文。
  2. 函數執行上下文 函數執行上下文只有在函數調用階段纔會被建立並放到調用棧中,函數執行完畢就會從調用棧中釋放。在全局做用域沒法訪問函數做用域中定義的變量,就是由於函數中的變量只在函數調用生命週期中存在。(閉包狀況例外,下文解釋)
  3. eval函數執行上下文 運行在 eval 函數中的代碼也得到了本身的執行上下文,Javascript不推薦使用eval函數。

如何工做

執行上下文分爲兩個階段,建立階段和執行階段。bash

建立階段

建立階段主要完成三件事情。閉包

  1. this綁定(這解釋了爲何this的值取決於函數的調用方式)
  2. 建立詞法環境。
  3. 建立變量環境

this綁定

根據當前函數的執行環境來綁定函數的this值,好比做爲對象屬性調用,this的值將被綁定到對象實例上。函數

詞法環境

定義:詞法環境是一種規範類型,基於 ECMAScript 代碼的詞法嵌套結構來定義標識符與特定變量和函數的關聯關係。詞法環境由環境記錄(environment record)和可能爲空引用(null)的外部詞法環境組成。post

詞法環境的組成 詞法環境分爲兩個部分:環境記錄;對外部環境的引用。學習

  1. 環境記錄是指存儲變量和函數聲明的實際位置
  2. 對外部環境引用意味着他能夠訪問其餘詞法做用域,閉包原理基於此。

詞法環境分爲兩種:
1)全局環境:在全局執行上下文中,是一個沒有外部環境的詞法環境。其外部引用爲null。它擁有一個全局對象(window 對象)及其關聯的方法和屬性(例如數組方法)以及任何用戶自定義的全局變量,this 的值指向這個全局對象。
2)函數環境:用戶在函數中定義的變量被存儲在環境記錄中,其外部引用能夠是函數環境也能夠是全局環境。對於函數環境而言,還包含一個arguments對象,該對象包含函數參數的引用,是一個類數組結構,如this

function foo(a, b) {  
  var c = a + b;  
}  
foo(2, 3);

// arguments 對象  
Arguments: {0: 2, 1: 3, length: 2}
複製代碼

環境變量分爲兩種spa

  • 聲明性環境記錄 存儲變量、函數和參數。一個函數環境包含聲明性環境記錄。
  • 對象環境記錄 用於定義在全局執行上下文中出現的變量和函數的關聯。全局環境包含對象環境記錄。

變量環境

變量環境也是詞法法環境,用於存儲var聲明的變量。

何謂聲明提高

執行上下文在建立階段,會掃描並解析變量和函數聲明。其中函數聲明存儲在環境中,而變量會被設置爲 undefined(在 var 的狀況下)或保持未初始化(在 let 和 const 的狀況下)。這就是所謂的聲明提高

執行階段

在此階段,完成對全部變量的分配,最後執行代碼。若是找不到let變量的值,將會爲其分配undefined。

小結

理解函數執行上下文,可讓咱們知道一段js代碼按照什麼樣的順序被執行和調用。有助於咱們理解聲明提高,閉包,函數做用域,this綁定等等概念。歸納起來就是:

  1. js引擎在js首頁被解析的時候,會建立一個全局執行上下文,這個全局上下文貫穿頁面的整個生命週期。
  2. 函數被調用的時候,會建立函數的執行上下文,包含環境記錄(收集函數聲明和變量定義)和環境上下文引用(外部環境--閉包或者全局上下文)。函數執行完畢,函數上下文就會被移出調用棧。
  3. 執行上下文有兩個階段,建立階段會綁定this,收集函數聲明和變量定義以及外部環境引用。執行階段會進行變量賦值與函數調用。

參考:【譯】理解 Javascript 執行上下文和執行棧

相關文章
相關標籤/搜索