JavaScript程式碼在執行時會進入一個執行環境,這是我們都知道的。這種執行環境我們也叫做執行上下文(Execution Context)。
javascript中常見的執行環境有三種:
1、全域性環境:程式碼執行首先進入的就是全域性環境。
2、函式環境:當函式執行時,就會進入當前函式中執行程式碼。
3、eval環境:不做解釋,可以參考《你不知道javascript上》。
因此可以預見的是,在一段javascript程式碼執行時,必定會有不少執行環境的出現。
javascript引擎會以棧的方式來處理這些執行環境,這個棧,就是call stack(函式呼叫棧)。call stack規定javascript程式碼執行的順序。棧底永遠都是全域性上下文(全域性環境),棧頂則是當前正在執行的上下文。
當程式碼遇到以上幾種情況,都會生產執行上下文並進入call stack。處於棧頂的執行上下文執行完畢後會自動退出call stack。(這也符合了我們所說的棧的“兵乓球盒模型”)。
為了清晰的看到整個程式碼執行的過程,我們通過幾個例項來了解call stack的規則。
為了方便學習js核心,推薦這個工具:latentflip.com/loupe。簡直六的不行!
3.1 例項1
var color = 'blue';
function changeColor() {
var anotherColor = 'red';
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColor();
複製程式碼
在我們開始瞭解之前,我們應該自己心裡先把流程跑一遍。
好了,現在開始
第一步全域性上下文入棧,並一直存於棧底。(由於loupe工具沒有顯示全域性環境,所以這裡就不上圖了。
第二步,全域性上下文入棧後,從可執行程式碼開始執行(在程式碼執行之前還有些活動,我稍後系列中會做筆記),直到遇到了changeColors(),這句程式碼啟用了函式changeColors,從而建立了自己的執行上下文。因而此時是changeColorsEC的上下文入棧。如下圖所示:
第三步,changeColorsEC的上下文入棧後,開始執行可執行的程式碼,當遇到swapColors()這句程式碼時啟用了swapColors的執行上下文。因此第三步就是changeColorsEC的上下文入棧。如下圖所示:
第四步,在swapColors的可執行程式碼中,沒有其他能生成執行上下文的情況,因此這段程式碼順利結束自己,他會從call stack中彈出。
第五步,在swapColors的執行上下問彈出後,在changeColors執行上下文中,繼續執行可執行的程式碼,沒有在遇到其它的執行上下文,順利彈出。這樣整個,call stack中就只剩下全域性上下文了。
最後,關閉瀏覽器視窗,全域性上下文彈出棧。
3.2 例項2
function f1() {
var n = 999;
function f2() {
alert(n);
}
return f2;
}
var result = f1();
result();
複製程式碼
這是一個簡單的閉包例項,我們只需根據“函式執行時,執行上下文被啟用”。這一原則繼續在我們心裡走一遍程式碼執行過程。
第一步仍然是全域性上下文入棧。
第二步,執行可執行的程式碼,當遇到f1()時,f1的執行上下被啟用,併入棧。
第三步,在f1中執行可執行的程式碼,沒有可執行的上下文,彈出棧來。
第四步,繼續在全域性上下文中,執行可執行的程式碼,這個時候遇到了result(),result()會建立一個的新的上下文(被啟用),因此這時result的上下文入棧。
第五步,這個result()其實就是在f1中宣告的函式f2,因此這個時候會執行f2中的程式碼。由於在f2中沒有產生新的上下文,因此執行完畢後直接出棧。
3.3 生命週期
我們知道,當一個函式呼叫時,一個新的執行上下會被建立。一個執行上下文的生命週期大致可以分為兩個階段:建立階段和執行階段
建立階段
在這個階段,執行上下文會分別建立變數物件,確認作用域鏈,以及確定this的指向。
執行階段
建立階段之後,就開始執行程式碼,這個時候會完成變數賦值、函式引用、以及執行其它可執行的程式碼。
從執行上下文的生命週期可以看到它的重要性,其中涉及了變數物件、作用域鏈、this等許多重要但並不容易搞清楚的概念,這些概念有助於我們真正理解javascript程式碼的執行機制。我會在之後的系列中持續上傳我的學習筆記。@陽波大神
記住:執行上下文(全域性、函式)、函式啟用會幹嘛?、執行上下文的生命週期