深入理解JavaScript 執行上下文

木子星兮發表於2020-07-20

只有理解了執行上下文,才能更好地理解 JavaScript 語言本身,比如變數提升,作用域,閉包等

執行上下文

執行上下文是當前程式碼的執行環境。

執行上下文主要是三種型別:

  1. 全域性執行上下文:全域性執行環境是最外圍的一個執行環境,在瀏覽器的全域性物件是 window, this指向這個物件
  2. 函式執行上下文:可以有無數個,函式被呼叫的時候會被建立。每次呼叫函式都會建立一個新的執行上下文。
  3. eval執行上下文,很少用。

每個執行上下文,都有三個重要屬性:

  1. 變數物件 (variable object, VO): 每個執行環境都有一個與之關聯的變數物件,環境中定義的所有變數和函式都儲存在這個物件中。雖然我們編寫的程式碼無法訪問這個物件,但解析器在處理資料時會在後臺使用它。
在函式上下文中,使用活動物件 (activation object, AO) 來表示變數物件。活動物件和變數物件其實是一個東西,只有當進入一個執行環境時,這個執行上下文的變數物件才會被啟用,此時稱為 活動物件(AO),只有活動物件上的屬性才能被訪問。
  1. 作用域鏈(scope chain):當程式碼在一個環境中執行時,會建立變數物件的一個作用域鏈。作用域鏈的用途,是保證對執行環境有權訪問的所有變數和函式的有序訪問。
  2. this

執行上下文的生命週期:建立 -> 執行 -> 回收

1. 建立階段:

1.1 建立變數物件:
  • 初始化函式的引數 arguments
  • 函式宣告
  • 變數宣告

舉個簡單的例子來理解變數物件

function getName(name) {
    var b = 2;
    function foo() {};
    var bar = function() {};

}
getName('lucystar')

此時的 AO 大致如下

AO = {
    arguments: {
        0: 'lucystar',
        length: 1
    },
    name: 'lucystar',
    b: undefined,
    foo: reference to function foo(){},
    bar: undefined
}
上面例子中涉及到了變數提升和函式提升,之前在 從JS底層理解var、let、const這邊文章中也介紹過
1.2 建立作用域鏈

函式的作用域在函式定義的時候就確定了。作用域鏈本身包含變數物件,當查詢變數時,會先從當前上下文中的變數物件中查詢,如果沒有找到,就會從父級執行上下文的變數物件中查詢,一直找到全域性執行上下文的變數物件

1.3 確定this的指向

這部分又分為多種情況,具體的可以檢視另一篇文章 一文理解this&call&apply&bind

2. 執行階段

執行變數賦值,程式碼執行

3. 回收階段

執行上下文出棧被垃圾回收機制進行回收。關於記憶體回收的內容,可以檢視 V8記憶體管理及垃圾回收機制

執行上下文棧

執行上下文棧是用來管理執行上下文的。在執行上下文建立好後,JavaScript引擎會將執行上下文壓入到棧中,通常把這種用來管理執行上下文的棧稱為執行上下文棧,又稱呼叫棧。

let a = 'javascript';

function foo() {
    console.log('foo');
    bar();
}
function bar() {
    console.log('bar');
}
foo();

執行上下文棧

  1. 上述程式碼在瀏覽器載入時,JavaScript 引擎建立了一個全域性執行上下文並把它壓入到當前執行棧。
  2. 當遇到 foo() 函式呼叫時, JavaScript 引擎建立了一個 foo 函式執行上下文並把它壓入到當前執行棧的頂部。
  3. 當從 foo() 函式內部呼叫 bar() 函式時,JavaScript 引擎建立了一個 bar 函式執行上下文並把它壓入到當前執行棧的頂部。
  4. 當函式 bar 執行完畢,它的執行上下文會從當前棧中彈出,控制流程到達下一個執行上下文,即 foo() 函式的執行上下文。
  5. 當 foo() 執行完成,它的執行上下文從棧彈出,控制流程到達全域性執行上下文,一旦所有程式碼執行完成,javaScript 引擎就從當前棧中移除全域性執行上下文。
為什麼基本資料型別儲存在棧中,引用資料型別儲存在堆中?JavaScript引擎需要用棧來維護程式執行期間的上下文的狀態,如果棧空間大了的話,所有資料都存放在棧空間裡面,會影響到上下文切換的效率,進而影響整個程式的執行效率。

參考

其他

最近發起了一個100天前端進階計劃,主要是深挖每個知識點背後的原理,歡迎關注 微信公眾號「牧碼的星星」,我們一起學習,打卡100天。同時也會分享一些自己學習的一些心得和想法,歡迎大家一起交流。

相關文章