執行上下文(也稱執行環境execution context)(簡稱:EC)是個抽象的概念,是在函數被調用時,可是在函數體被真正執行之前所建立的。每一個執行上下文都有一個變量對象(variable object),保存着當前環境中全部的變量或函數聲明或形參arguments以及this。前端
一系列活動的執行上下文從邏輯上造成一個棧。棧底老是全局上下文,棧頂是當前(活動的)執行上下文。當執行流進入函數時,函數的環境就會被推入一個環境棧中,在函數執行以後,棧再將其環境彈出,把控制權返回給以前的執行環境。
代碼在一個環境中執行,會建立變量對象的一個做用域鏈,做用域鏈保證了執行的順序。git
只存在一個全局的上下文,該上下文能被任何其它的上下文所訪問到。github
每調用執行一個函數時,引擎就會自動新建出一個函數執行上下文,就是新建一個局部做用域,能夠在該局部做用域中聲明私有變量等,在外部的上下文中是沒法直接訪問到該局部做用域內的元素的。
當在全局上下文中調用執行一個函數時,程序流就進入該被調用函數內,此時引擎就會爲該函數建立一個新的執行上下文(即便是調用自身函數),而且將其壓入到執行上下文堆棧的頂部。瀏覽器老是執行當前在堆棧頂部的上下文,一旦執行完畢,該上下文就會從堆棧頂部被彈出,而後,進入最新的堆棧頂部的上下文執行代碼。堆棧中的上下文就會被依次執行而且彈出堆棧,直到回到全局的上下文。瀏覽器
在eval函數內運行的代碼。函數
發生在調用一個函數時,可是在執行函數體內具體的代碼之前。this
先是處理arguments參數,接着是函數的聲明,最後是變量的聲明。code
創建arguments對象,檢查當前上下文中的參數,創建該對象下的屬性以及屬性值。每一個執行環境都有一個與之關聯的變量對象(variable object),環境中定義的全部變量和函數都保存在這個對象中。雖然咱們編寫的代碼沒法訪問這個對象,但解析器在處理數據時會在後臺使用它。對象
檢查當前上下文中的函數聲明:找到函數聲明的函數名,在variableObject下面用該函數名創建一個屬性,屬性值就是指向該函數在內存中的地址的一個引用;若是上述函數名已經存在於variableObject下,那麼對應的屬性值會被新的引用所覆蓋。blog
檢查當前上下文中的變量聲明:每找到一個變量的聲明,就在variableObject下,用變量名創建一個屬性,屬性值爲undefined。若是該變量名已經存在於variableObject屬性中,直接跳過(防止指向函數的屬性的值被變量屬性覆蓋爲undefined),原屬性值不會被修改。ip
每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。 ECMAScript 程序中的執行流正是由這個方便的機制控制着。當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈(scope chain)。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。做用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。若是這個環境是函數,則將其活動對象(activation object)做爲變量對象。活動對象在最開始時只包含一個變量,即 arguments 對象(這個對象在全局環境中是不存在的)。做用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是做用域鏈中的最後一個對象。
肯定this指向window或當前對象。
function aoo(i){ var a = 'staven'; var b = function boo(){ }; function coo(){ } } aoo(1);
aooExecutionContext = { variableObject:{ arguments:{ 0:1, length:1 }, i:1, coo:(指向function coo()), a:undefined, b:undefined }, scopeChain:{……}, this:{……} };
aooExecutionContext = { variableObject:{ arguments:{ 0:1, length:1 }, i:1, coo:(指向function coo()), a:"staven", b:(指向function boo()) }, scopeChain:{……}, this:{……} };
(function(){ console.log(typeof foo); //function console.log(typeof bar); //undefined var foo = 'hello', bar = function(){ return "staven"; }; function foo(){ return "hello"; } console.log(typeof foo); //string console.log(typeof bar); //function }());
由於在上下文的創建階段,先是處理arguments, 參數,接着是函數的聲明,最後是變量的聲明。那麼,發現foo函數的聲明後,就會在variableObject下面創建一個foo屬性,其值是一個指向函數的引用。當處理變量聲明的時候,發現有var foo的聲明,可是variableObject已經具備了foo屬性,因此直接跳過。當進入代碼執行階段的時候,就能夠經過訪問到foo屬性了,由於它已經就存在,而且是一個函數引用。
bar是變量的聲明,在創建階段的時候,被賦予的默認的值爲undefined。因爲它只要在代碼執行階段纔會被賦予具體的值,因此,當調用typeof(bar)的時候輸出的值爲undefined。