只有理解了執行上下文,才能更好地理解 JavaScript 語言本身,比如變數提升,作用域,閉包等
執行上下文
執行上下文是當前程式碼的執行環境。
執行上下文主要是三種型別:
- 全域性執行上下文:全域性執行環境是最外圍的一個執行環境,在瀏覽器的全域性物件是 window, this指向這個物件
- 函式執行上下文:可以有無數個,函式被呼叫的時候會被建立。每次呼叫函式都會建立一個新的執行上下文。
- eval執行上下文,很少用。
每個執行上下文,都有三個重要屬性:
- 變數物件 (variable object, VO): 每個執行環境都有一個與之關聯的變數物件,環境中定義的所有變數和函式都儲存在這個物件中。雖然我們編寫的程式碼無法訪問這個物件,但解析器在處理資料時會在後臺使用它。
在函式上下文中,使用活動物件 (activation object, AO) 來表示變數物件。活動物件和變數物件其實是一個東西,只有當進入一個執行環境時,這個執行上下文的變數物件才會被啟用,此時稱為 活動物件(AO),只有活動物件上的屬性才能被訪問。
- 作用域鏈(scope chain):當程式碼在一個環境中執行時,會建立變數物件的一個作用域鏈。作用域鏈的用途,是保證對執行環境有權訪問的所有變數和函式的有序訪問。
- 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();
- 上述程式碼在瀏覽器載入時,JavaScript 引擎建立了一個全域性執行上下文並把它壓入到當前執行棧。
- 當遇到
foo()
函式呼叫時, JavaScript 引擎建立了一個 foo 函式執行上下文並把它壓入到當前執行棧的頂部。 - 當從
foo()
函式內部呼叫bar()
函式時,JavaScript 引擎建立了一個 bar 函式執行上下文並把它壓入到當前執行棧的頂部。 - 當函式 bar 執行完畢,它的執行上下文會從當前棧中彈出,控制流程到達下一個執行上下文,即
foo()
函式的執行上下文。 - 當 foo() 執行完成,它的執行上下文從棧彈出,控制流程到達全域性執行上下文,一旦所有程式碼執行完成,javaScript 引擎就從當前棧中移除全域性執行上下文。
為什麼基本資料型別儲存在棧中,引用資料型別儲存在堆中?JavaScript引擎需要用棧來維護程式執行期間的上下文的狀態,如果棧空間大了的話,所有資料都存放在棧空間裡面,會影響到上下文切換的效率,進而影響整個程式的執行效率。
參考
- JavaScript深入之執行上下文棧
- JavaScript深入之執行上下文
- JavaScript深入之變數物件
- 深入理解JavaScript系列(11):執行上下文(Execution Contexts)
- 《JavaScript高階程式設計 (第三版)》
其他
最近發起了一個100天前端進階計劃,主要是深挖每個知識點背後的原理,歡迎關注 微信公眾號「牧碼的星星」,我們一起學習,打卡100天。同時也會分享一些自己學習的一些心得和想法,歡迎大家一起交流。