概念 執行環境(Execution context,簡稱EC)或執行上下文對象(後面統一用執行上下文表示),它定義了變數或者函數有權訪問的其他數據,決定了他們各自的行為。是不是有點不好理解,那我先簡單翻譯下: js代碼執行時所在的環境。繼續後面 在JavaScript中執行環境分三種: 執行上下文棧 ...
概念
執行環境(Execution context,簡稱EC)或執行上下文對象(後面統一用執行上下文表示),它定義了變數或者函數有權訪問的其他數據,決定了他們各自的行為。是不是有點不好理解,那我先簡單翻譯下: js代碼執行時所在的環境。繼續後面
在JavaScript中執行環境分三種:
- 全局執行環境 : 這個是最外圍的執行環境,一旦代碼被載入,引擎最先進入的就是這個環境。在瀏覽器中,全局環境就是window對象,因此所有全局屬性和函數都是作為window對象的屬性和方法創建。全局執行環境直到應用程式退出時才會被銷毀。
- 函數執行環境:當執行流執行一個函數時,javascript會創建一個新的函數執行環境,函數執行環境中的代碼執行完之後,該環境銷毀,保存在其中的所有變數和函數定義也被之銷毀。
- Eval(一個普通函數,但是他有一個快速通道通向編譯器,可以將string變成可執行的代碼)執行環境 : Eval的執行環境和函數調用的執行環境相同。
執行上下文棧
那麼現在問題來了,平時工作中寫的全局變數,函數以及嵌套函數應該不少了吧,每執行一個函數就會創建一個新的函數執行上下文,想想那麼多執行上下文是不是有點恐怖,那麼js又是如何管理那麼多的執行上下文呢?
當執行流執行一個函數時,就會給當前函數創建執行上下文,並且將該執行上下文被推入一個執行上下文棧中(Execution context stack,ECS),在函數執行完之後,執行上下文棧將被彈出,並且把控制器返回給之前執行的執行上下文;
根據工作原理執行上下文棧類似一個數組結構 ,我們模擬執行上下文棧的行為:
1 ECStack = []; //先定義執行上下文棧是一個數組:
JavaScript 開始要解釋執行代碼的時候,最先遇到的就是全局代碼,所以初始化的時候首先就會向執行上下文棧壓入一個全局執行上下文,用 globalEC表示它,並且只有當整個應用程式結束的時候,ECStack 才會被清空,所以 ECStack 最底部永遠有個 globalEC:
1 ECStack = [ 2 globalEC 3 ];
現在 JavaScript 遇到下麵的這段代碼了:
1 <script> 2 function run3() { 3 console.log('run3') 4 } 5 6 function run2() { 7 run3(); 8 } 9 10 function run1() { 11 run2(); 12 } 13 run1(); 14 </script>
當執行一個函數的時候,就會創建一個執行上下文,並且壓入執行上下文棧,當函數執行完畢的時候,就會將函數的執行上下文從棧中彈出。知道了這樣的工作原理,讓我們來看看如何處理上面這段代碼:
1 // 偽代碼 2 3 // run1() 推入執行上下文棧 4 ECStack.push(run1.EC); 5 6 // run1中調用了run2,創建run2的執行上下文 推入執行上下文棧 7 ECStack.push(run2.EC); 8 9 // run2還調用了run3,創建run3的執行上下文 推入執行上下文棧 10 ECStack.push(run3.EC); 11 12 // run3執行完畢 13 ECStack.pop(); 14 15 // run2執行完畢 16 ECStack.pop(); 17 18 // run1執行完畢 19 ECStack.pop(); 20 21 // javascript接著執行下麵的代碼,但是ECStack底層永遠有個globalContext,直到應用程式退出(例如關閉網頁)
回顧定義
執行環境(Execution context,簡稱EC)或執行上下文對象,它定義了變數或者函數有權訪問的其他數據,決定了他們各自的行為。
當執行流進入一個函數時,創建執行上下文對象,然後推入執行上下文棧。是不是感覺跟定義相差太遠,難道是《javascript高程程式設計》寫錯了?
當然不是,執行上下文到底包含了哪些內容所以歡迎閱讀下一篇《javascript 之變數對象》。