執行上下文,即context,也不知道是誰翻譯的,很多的文獻、書籍用的都是這個詞。還記得第一次接觸這個詞時的惆悵、迷惘、不知所措,扶了扶眼鏡,翻開大辭典,仍是翻譯成環境比較接地氣。JS執行上下文,即JS的執行環境。web
當咱們的代碼執行時,會進入到不一樣的執行上下文,即不一樣的環境。在不一樣的環境中,有着不一樣的 scope(做用域),代碼所能訪問到的資源也就不一樣。 在 JS 中,執行環境有以下三種狀況:瀏覽器
全局環境bash
代碼默認運行的環境,代碼執行時會首先進入全局環境。它是最外圍的一個執行環境,根據 ECMAScript 實現所在的宿主環境的不一樣,表示全局環境的對象也不同。在 web 瀏覽器中,全局環境就是 window 對象。全局變量和函數都是做爲全局對象 window 的變量和方法來建立的。函數
函數環境post
函數被調用執行時,所建立的執行環境。ui
evalthis
使用 eval 會進入一個新的執行環境,它的變量對象爲全局變量對象或調用者的變量對象。因爲 eval 的毒瘤屬性,通常不推薦使用,可忽略。spa
某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在環境中的變量和函數也隨之銷燬。這些變量和函數保存在一個叫作變量對象的對象(variableObject)中,關於變量對象將在變量對象與做用域鏈一文做詳細探討。線程
執行環境的生命週期大概分爲兩個階段,即建立階段和執行階段:翻譯
1. 建立階段
由此,一個執行環境能夠由包含做用域鏈、變量對象和 this 指針的對象組成:
executionContextObj = {
scopeChain: {},
variableObject: {},
this: {}
}
複製代碼
2. 代碼執行階段
瀏覽器中的解釋器被實現爲單線程,同一時間只能處理一個任務,JS 程序中多個執行環境會以棧的方式來處理,這個棧叫作執行棧。棧底永遠都是全局環境(窗口關閉時彈出),棧頂就是當前正在執行的環境。前述三種狀況都會建立執行環境,執行環境建立時會被壓入棧頂,成爲一個運行(活動)的環境,位於棧頂的環境執行完畢後就從棧頂彈出,並將環境控制權交給調用者(以前的棧),而調用者繼續執行(或激活其餘環境),直到它的執行環境結束。ECMAScript 程序中的執行流正是由這個方便的機制控制着。
來看下面的例子:
var firstName = 'snow';
function getName() {
var lastName = 'John';
function fullName() {
var name = lastName + firstName;
return name;
}
var name = fullName();
return name;
}
getName();
複製代碼
其執行棧變化過程以下:
首先,將全局環境壓入棧,開始執行代碼,
直到遇到getName()
,準備調用函數,建立函數 getName 的執行環境,將其壓入棧頂並開始執行函數
直到遇到fullName()
,準備調用函數,建立函數 fullName 的執行環境,將其壓入棧頂並開始執行函數
函數 fullName 執行時沒有再生成執行環境,執行完畢後則從棧頂彈出
fullName 執行棧彈出後,控制權回到了 getName 的執行棧,繼續執行代碼,執行完畢,從棧頂彈出
最後回到了全局環境,窗口關閉後彈出