如果你是或者想成為一名 JavaScript 開發者,你必須知道 JavaScript 程序內部是如何執行的。理解執行上下文和執行棧對于理解其他 JavaScript 概念(如變量聲明提升,作用域和閉包)至關重要。
正確理解執行上下文和執行棧的概念將使您成為更出色的 JavaScript 開發者。
閑話少說,讓我們開始吧 :)
分享自 Bit 的博客
使用 Bit 應用所提供的組件作為構建模塊,你就是架構師。隨時隨地和你的團隊分享、發現和開發組件,快來嘗試鮮!
Bit - 分享和創造代碼組件: Bit 能幫助你在不同項目和應用中分享、發現和使用代碼組件來創建新功能和……
什么是執行上下文?
簡而言之,執行上下文是評估和執行 JavaScript 代碼的環境的抽象概念。每當 Javascript 代碼在運行的時候,它都是在執行上下文中運行。
執行上下文的類型
JavaScript 中有三種執行上下文類型。
執行棧
執行棧,也就是在其它編程語言中所說的“調用?!保且环N擁有 LIFO(后進先出)數據結構的棧,被用來存儲代碼運行時創建的所有執行上下文。
當 JavaScript 引擎第一次遇到你的腳本時,它會創建一個全局的執行上下文并且壓入當前執行棧。每當引擎遇到一個函數調用,它會為該函數創建一個新的執行上下文并壓入棧的頂部。
引擎會執行那些執行上下文位于棧頂的函數。當該函數執行結束時,執行上下文從棧中彈出,控制流程到達當前棧中的下一個上下文。
讓我們通過下面的代碼示例來理解:
let a = 'Hello World!';function first() { console.log('Inside first function'); second(); console.log('Again inside first function');}function second() { console.log('Inside second function');}first();console.log('Inside Global Execution Context');
上述代碼的執行上下文棧。
當上述代碼在瀏覽器加載時,JavaScript 引擎創建了一個全局執行上下文并把它壓入當前執行棧。當遇到 first() 函數調用時,JavaScript 引擎為該函數創建一個新的執行上下文并把它壓入當前執行棧的頂部。
當從 first() 函數內部調用 second() 函數時,JavaScript 引擎為 second() 函數創建了一個新的執行上下文并把它壓入當前執行棧的頂部。當 second() 函數執行完畢,它的執行上下文會從當前棧彈出,并且控制流程到達下一個執行上下文,即 first() 函數的執行上下文。
當 first() 執行完畢,它的執行上下文從棧彈出,控制流程到達全局執行上下文。一旦所有代碼執行完畢,JavaScript 引擎從當前棧中移除全局執行上下文。
怎么創建執行上下文?
到現在,我們已經看過 JavaScript 怎樣管理執行上下文了,現在讓我們了解 JavaScript 引擎是怎樣創建執行上下文的。
創建執行上下文有兩個階段:1) 創建階段 和 2) 執行階段。
The Creation Phase
在 JavaScript 代碼執行前,執行上下文將經歷創建階段。在創建階段會發生三件事:
所以執行上下文在概念上表示如下:
ExecutionContext = { ThisBinding = <this value>, LexicalEnvironment = { ... }, VariableEnvironment = { ... },}
This 綁定:
在全局執行上下文中,this 的值指向全局對象。(在瀏覽器中,this引用 Window 對象)。
在函數執行上下文中,this 的值取決于該函數是如何被調用的。如果它被一個引用對象調用,那么 this 會被設置成那個對象,否則 this 的值被設置為全局對象或者 undefined(在嚴格模式下)。例如:
let foo = { baz: function() { console.log(this); }}foo.baz(); // 'this' 引用 'foo', 因為 'baz' 被 // 對象 'foo' 調用let bar = foo.baz;bar(); // 'this' 指向全局 window 對象,因為 // 沒有指定引用對象
詞法環境
官方的 ES6 文檔把詞法環境定義為
詞法環境是一種規范類型,基于 ECMAScript 代碼的詞法嵌套結構來定義標識符和具體變量和函數的關聯。一個詞法環境由環境記錄器和一個可能的引用外部詞法環境的空值組成。
簡單來說詞法環境是一種持有標識符―變量映射的結構。(這里的標識符指的是變量/函數的名字,而變量是對實際對象[包含函數類型對象]或原始數據的引用)。
現在,在詞法環境的內部有兩個組件:(1) 環境記錄器和 (2) 一個外部環境的引用。
詞法環境有兩種類型:
環境記錄器也有兩種類型(如上!):
簡而言之,
在全局環境中,環境記錄器是對象環境記錄器。
在函數環境中,環境記錄器是聲明式環境記錄器。
注意 ― 對于函數環境,聲明式環境記錄器還包含了一個傳遞給函數的 arguments 對象(此對象存儲索引和參數的映射)和傳遞給函數的參數的 length。
抽象地講,詞法環境在偽代碼中看起來像這樣:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // 在這里綁定標識符 } outer: <null> }}FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // 在這里綁定標識符 } outer: <Global or outer function environment reference> }}
變量環境:
它同樣是一個詞法環境,其環境記錄器持有變量聲明語句在執行上下文中創建的綁定關系。
如上所述,變量環境也是一個詞法環境,所以它有著上面定義的詞法環境的所有屬性。
在 ES6 中,詞法環境組件和變量環境的一個不同就是前者被用來存儲函數聲明和變量(let 和 const)綁定,而后者只用來存儲 var 變量綁定。
我們看點樣例代碼來理解上面的概念:
let a = 20;const b = 30;var c;function multiply(e, f) { var g = 20; return e * f * g;}c = multiply(20, 30);
執行上下文看起來像這樣:
GlobalExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // 在這里綁定標識符 a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: <null> }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // 在這里綁定標識符 c: undefined, } outer: <null> }}FunctionExectionContext = { ThisBinding: <Global Object>, LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // 在這里綁定標識符 Arguments: {0: 20, 1: 30, length: 2}, }, outer: <GlobalLexicalEnvironment> },VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", // 在這里綁定標識符 g: undefined }, outer: <GlobalLexicalEnvironment> }}
注意 ― 只有遇到調用函數 multiply 時,函數執行上下文才會被創建。
可能你已經注意到 let 和 const 定義的變量并沒有關聯任何值,但 var 定義的變量被設成了 undefined。
這是因為在創建階段時,引擎檢查代碼找出變量和函數聲明,雖然函數聲明完全存儲在環境中,但是變量最初設置為 undefined(var 情況下),或者未初始化(let 和 const 情況下)。
這就是為什么你可以在聲明之前訪問 var 定義的變量(雖然是 undefined),但是在聲明之前訪問 let 和 const 的變量會得到一個引用錯誤。
這就是我們說的變量聲明提升。
執行階段
這是整篇文章中最簡單的部分。在此階段,完成對所有這些變量的分配,最后執行代碼。
注意 ― 在執行階段,如果 JavaScript 引擎不能在源碼中聲明的實際位置找到 let 變量的值,它會被賦值為 undefined。
結論
我們已經討論過 JavaScript 程序內部是如何執行的。雖然要成為一名卓越的 JavaScript 開發者并不需要學會全部這些概念,但是如果對上面概念能有不錯的理解將有助于你更輕松,更深入地理解其他概念,如變量聲明提升,作用域和閉包。
就是這樣,如果你發現這篇文章有用,請點擊 👏 按鈕并在下面自由地評論!我很樂意和你討論 😃。
分享自Bit 的博客
Bit 使得在項目和應用中分享小型組件和模塊變得非常簡單,使您和您的團隊可以更快地構建代碼。隨時隨地和你的團隊分享、發現和開發組件,快來嘗鮮!
Bit - 分享和創造代碼組件: Bit 能幫助你在不同項目和應用中分享、發現和使用代碼組件來創建新功能和……
總結
以上所述是小編給大家介紹的JavaScript 中的執行上下文和執行棧 ,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
新聞熱點
疑難解答