執行上下文(Execution Context):函數執行前進行的準備工作(也稱執行上下文環境)。
當執行代碼進入一個環境時,就會爲該環境創建一個執行上下文,它會做一些準備工作,如變量提升,確定作用域等。
一,執行上下文的類型
js中有三種執行上下文類型:
全局執行上下文:頂層上下文,任何不在函數內部的代碼都在其中。上下文中的大哥大!它幹了兩件事:1.創建一個全局的window對象(瀏覽器環境下),2.設置this的值等於這個全局對象。(一個程序中只會有一個全局執行上下文)
函數執行上下文:函數被調用時,會創建新的上下文,可以有多個。
Eval函數執行上下文:aval函數專用,用的不多,沒排面,不討論。
執行棧
在js中,通過棧的存取方式來管理執行上下文,我們可稱其爲執行棧,或函數調用棧(Call Stack)。
棧遵循“先進先出,後進後出”的規則,也就是LIFO(Last In First Out)規則,可以用乒乓球盒子來類比理解。
特點:
1,先進先出,後進後出
2,出口在頂部,且僅有一個
當js引擎第一次遇到你的腳本時,它會創建一個全局的執行上下文並且壓入當前執行棧。而每當引擎遇到一個函數調用,它會爲該函數創建一個新的執行上下文並壓入棧的頂部。
引擎會執行那些位於執行上下文頂棧頂的函數,當函數執行結束時,其執行上下文從棧中彈出,控制流程達到當前棧中的下一個上下文。
我們可以通過示例代碼來理解:
function foo () {
function bar () {
return 'I am bar';
}
return bar();
}
foo();
二,執行上下文的生命週期
執行上下文的生命週期有兩個階段:
創建階段(進入執行上下文,如:變量提升等)
執行階段
創建階段的操作:
1,創建變量對象
函數環境會初始化創建Arguments對象(並賦值),並傳遞長度length
匿名函數聲明(並賦值)
1,變量聲明,函數表達式聲明(未賦值,即變量提升)
2,確定this指向(this由調用者確定)
3,確定作用域(詞法環境確定,哪裏聲明,哪裏確定)
執行階段的操作
1,變量對象賦值
變量賦值
函數表達式賦值
2,調用函數
3,執行其他代碼片段
值得注意的是,執行上下文可存在多個,因此在遞歸中可能因爲沒有終止條件而造成死循環,堆棧溢出錯誤。
// 遞歸調用自身
function foo() {
foo();
}
foo();
// 報錯: Uncaught RangeError: Maximum call stack size exceeded
文末總結:
1,js是單線程
2,棧頂的執行上下文處於執行中時,其他的需要排隊
3,處於底部的總是全局上下文,關閉頁面時出棧
4,函數執行上下文可存在多個,但應避免遞歸時堆棧溢出
5,函數調用時就會創建新的上下文,即使調用自身,同樣如此
參考文檔:
https://mp.weixin.qq.com/s/lAvyjfBZvX0E50QiW3aW7w
https://juejin.im/post/5ba32171f265da0ab719a6d7(並未討論其中的詞法環境)