JS之執行上下文與執行上下文棧
分享一下JS中很基礎也很重要的概念:執行上下文(Execution Context);
將我們的程式碼進行分類:
1.程式碼分類(位置):
* 全域性程式碼
* 函式(函式)程式碼.
2.全域性執行上下文
(1) 在執行全域性程式碼前將window確定為全域性執行上下文
(2)對全域性資料進行預處理
* var定義的全域性變數===>undefined,新增為window的屬性
* function宣告的全域性函式===>賦值(fun),新增為window的方法
* this===>賦值(window)
(3) 開始執行全域性程式碼
3.函式執行上下文
(1) 在呼叫函式,準備執行函式體之前,建立對應的函式執行上下文物件
(2) 對區域性資料進行預處理
* 形參變數==>賦值(實參),新增為執行上下文的屬性
* arguments==>賦值(實參列表),新增為執行上下文的屬性
* var定義的區域性變數==>undefined,新增為執行上下文的屬性
* function宣告的函式==>賦值(fun),新增為執行上下文的屬性
* this==>賦值(呼叫函式的物件)
(3) 開始執行函式體程式碼
// 全域性執行上下文 console.log(a1) // undefined console.log(a2) // 可以訪問到 var a1 = 3 function a2(){} // 函式執行上下文 function fn(b1){ console.log(b1) // 2 console.log(b2) // undefined console.log(b3) b3() console.log(this) // window console.log(arguments) //[2,3]偽陣列 var b2 = 3 function b3(){ console.log('b3') } } fn(2,3)
JS中並沒有嚴格意義上區分棧記憶體和堆記憶體,因此我們可以簡單粗暴的理解為JavaScript的所有資料都儲存在堆記憶體中。但是在某些場景,我們仍然需要基於堆疊資料結構的思維來實現一些功能,比如JavaScript的執行上下文.執行上下文的執行順序借用了棧資料結構的存取方式。(也就是我們說的函式呼叫棧).
棧的存取方式是先進後出.
當代碼的執行過程中,遇到全域性程式碼和函式程式碼都會生成一個執行上下文放入棧中,而處於棧頂的上下文執行完畢之後,就會自動出棧。
執行上下文棧:
1.在全域性程式碼執行前,JS就會建立一個棧來儲存管理所有的執行上下文物件
2.在全域性執行上下文(window)確定後,將其新增到棧中(壓棧)
3.在函式執行上下文建立後,將其新增到棧中(壓棧)
4.噹噹前函式執行完後,將棧頂的物件移除(出棧)
5.當所有的程式碼執行完後,棧中只剩window(全域性上下文)
注意: 1.全域性上下文在瀏覽器視窗關閉後出棧
2.函式執行中,遇到return直接終止可執行的程式碼,會直接將當前上下文彈出棧
function f1(){
var n=999;
function f2(){
console.log(n);
}
return f2;
}
var result=f1();
result(); // 999
console.log(result)
我們分析一下這段程式碼中執行上下文的順序:
第一步,首先是全域性上下文入棧
全域性上下文入棧之後,其中的可執行程式碼開始執行,直到遇到了f1();
這一句啟用函式f1
建立它自己的執行上下文,因此第二步就是f1的執行上下文入棧。
f1中的可執行程式碼並沒有呼叫f2,所以沒有建立新的上下文,直到return f2時才建立.這時候f1可執行程式碼完畢,出棧
遇到result(),建立result的執行上下文
執行result中的可執行程式碼,輸出n,執行完畢後,result出棧
這個時候只剩下全域性上下文,全域性上下文在瀏覽器視窗關閉後出棧.
對執行上下文總結一下:
- 執行上下文是單執行緒的
- 是同步執行,只有棧頂的上下文處於執行中,其他上下文需要等待
- 全域性上下文只有唯一的一個,它在瀏覽器關閉時出棧
- 函式的執行上下文的個數沒有限制
- 每次某個函式被呼叫,就會有個新的執行上下文為其建立,即使是呼叫的自身函式,也是如此。