调用堆栈
JavaScript 引擎是什么?
JavaScript 引擎說起來最流行的當然是谷歌的 V8 引擎了, V8 引擎使用在 Chrome 以及 Node 中,下面有個簡單的圖能說明他們的關系:
這個引擎主要由兩部分組成:
引擎怎么運行?
所以說我們還有很多引擎之外的 API,我們把這些稱為瀏覽器提供的 Web API,比如說 DOM、AJAX、setTimeout等等。
然后我們還擁有如此流行的事件循環和回調隊列。
為什么要知道堆棧?
如果一個項目越來越依賴 JavaScript,這就意味著開發人員必須利用這些語言和生態系統提供更深層次的核心內容去構建一個令人振奮的應用。然而,事實證明,有很多的開發者每天都在使用 JavaScript,但是卻不知道在底層 JavaScript 是怎么運作的。
什么是調用堆棧?
JavaScript 是一門單線程的語言,這意味著它只有一個調用棧,因此,它同一時間只能做一件事。
一個調用堆棧 是一個解釋的機制(如在Web瀏覽器中的JavaScript解釋器)
調用棧是一種數據結構,它記錄了我們在程序中的位置。如果我們運行到一個函數,它就會將其放置到棧頂。當從這個函數返回的時候,就會將這個函數從棧頂彈出,這就是調用棧做的事情。
- 當腳本調用函數時,解釋器將其添加到調用堆棧,然后開始執行該函數。
- 該函數調用的任何函數都會進一步添加到調用堆棧中,并在到達其調用的位置運行。
- 當前函數完成后,解釋器將其從堆棧中取出并在最后一個代碼清單中從中斷處繼續執行。
- 如果堆棧占用的空間超過分配給它的空間,則會導致“堆棧溢出”錯誤。
(1)例一:
function greeting() {// [1] Some codes heresayHi();// [2] Some codes here}function sayHi() {return "Hi!";}// Invoke the `greeting` functiongreeting();// [3] Some codes here忽略所有功能,直到它到達greeting()功能。
調用該greeting()功能。
將greeting函數添加到調用堆棧列表中。
調用堆棧列表:
greeting
執行greeting函數中的所有代碼行。
開始使用該sayHi()功能。
將該sayHi()函數添加到調用堆棧列表中。
調用堆棧列表:
greeting
sayHi
執行sayHi()函數內的所有代碼行,直到結束。
將執行返回到調用的行sayHi()并繼續執行greeting()函數的其余部分。
sayHi()從我們的調用堆棧列表中刪除該函數。
調用堆棧列表:
greeting
當greeting()函數內部的所有內容都被執行后,返回其調用行繼續執行其余的JS代碼。
greeting()從調用堆棧列表中刪除該函數。
調用堆棧列表:
EMPTY
我們從一個空的調用堆棧開始,每當我們調用一個函數時,它會自動添加到調用堆棧中,在執行所有代碼后,它會自動從調用堆棧中刪除。最后,我們最終得到了一個空堆棧。
(2)例二:
什么是堆棧溢出?
“堆棧溢出”,當你達到調用棧最大的大小的時候就會發生這種情況,而且這相當容易發生,特別是在你寫遞歸的時候卻沒有全方位的測試它。
例如:
function foo() {foo();}foo();當我們的引擎開始執行這段代碼的時候,它從 foo 函數開始。然后這是個遞歸的函數,并且在沒有任何的終止條件的情況下開始調用自己。因此,每執行一步,就會把這個相同的函數一次又一次地添加到調用堆棧中。然后它看起來就像是這樣的:
在單個線程上運行代碼很容易,因為你不必處理在多線程環境中出現的復雜場景——例如死鎖。
什么是并發與事件循環?
調用棧中的函數調用需要大量的時間來處理,那么這會發生什么情況呢?例如,假設你想在瀏覽器中使用 JavaScript 進行一些復雜的圖片轉碼。
事實上,問題是當調用棧有函數要執行,瀏覽器就不能做任何事,它會被堵塞住。這意味著瀏覽器不能渲染,不能運行其他的代碼,它被卡住了。如果你想在應用里讓 UI 很流暢的話,這就會產生問題。
??而且這不是唯一的問題,一旦你的瀏覽器開始處理調用棧中的眾多任務,它可能會停止響應相當長一段時間。大多數瀏覽器都會這么做,報一個錯誤,詢問你是否想終止 web 頁面。
什么是執行上下文?
簡而言之,執行上下文是評估和執行 JavaScript 代碼的環境的抽象概念。每當 Javascript 代碼在運行的時候,它都是在執行上下文中運行。
執行上下文的類型?
全局執行上下文 — 這是默認或者說基礎的上下文,任何不在函數內部的代碼都在全局上下文中。它會執行兩件事:創建一個全局的 window 對象(瀏覽器的情況下),并且設置 this 的值等于這個全局對象。一個程序中只會有一個全局執行上下文。
函數執行上下文 — 每當一個函數被調用時, 都會為該函數創建一個新的上下文。每個函數都有它自己的執行上下文,不過是在函數被調用時創建的。函數上下文可以有任意多個。每當一個新的執行上下文被創建,它會按定義的順序(將在后文討論)執行一系列步驟。
Eval 函數執行上下文 — 執行在 eval 函數內部的代碼也會有它屬于自己的執行上下文,但由于 JavaScript 開發者并不經常使用 eval,所以在這里我不會討論它。
總結
- 上一篇: 活期存款利率 各银行活期存款的利率
- 下一篇: 前端基础知识2