JavaScript工作原理:引擎,運行時,調用堆棧

JavaScript工作原理:引擎,運行時,調用堆棧

概述

幾乎每個人都已經聽說過V8引擎這個概念,而且大多人都知道JavaScript是單線程的,並且使用回調隊列

這篇文章中,我們將詳細介紹這些概念,並解釋JavaScript實際運行的原理。 通過了解這些詳細信息,將能夠編寫更好的非阻塞應用程序,以正確利用所提供的API。

JavaScript引擎

最流行的JavaScript引擎莫過於Google的V8引擎,Chrome瀏覽器和Node.js都是基於V8引擎的。簡略的示意圖:

JavaScript工作原理:引擎,運行時,調用堆棧

V8引擎包含兩個主要組件:

內存堆(Heap):負責內存分配

調用棧(Stack):代碼執行時,維護堆棧幀(Stack Frames)

運行時

幾乎所有的JavaScript開發者都使用過setTimeout這樣的API,然而這些APIs不是由V8引擎提供的。

JavaScript工作原理:引擎,運行時,調用堆棧

我們已經有了V8引擎,但實際上還有更多。我們把那些由瀏覽器提供的接口稱為Web APIs,比如DOM,AJAX,setTimeout等。

接下來是事件循環(event lool)和回調隊列(callback queue)。

調用棧(Call Stack)

JavaScript是一種單線程變成語言,這意味著它只有一個調用棧。因此,在同一時間它只能做一件事。

調用棧是一個數據結構,它會記錄代碼執行的位置。例如我們執行進入一個函數,我們會把這個函數放在堆棧的頂部,函數執行結束返回之後,我們把這個函數從堆棧中移除。這就是調用棧的功能。

簡單代碼示例:

function multiply(x, y) { return x * y;}function printSquare(x) { var s = nultiply(x, x); console.log(s);}printSquare(5);

當引擎執行這段代碼時,調用棧為空,之後運行如下:

JavaScript工作原理:引擎,運行時,調用堆棧

每個Step叫做堆棧幀(Stack Frame)。

調用棧就是通過堆棧幀來追蹤異常,堆棧幀基本就是調用棧出現異常時候的狀態。示例代碼如下:

function foo() { throw new Error('SessionStack will help you resolve crashes :)');}function bar() { foo();}function start() { bar();}

假設上面代碼保存在foo.js文件,執行上面代碼在Chrome瀏覽器中,Error的堆棧信息會如下圖打印出來:

JavaScript工作原理:引擎,運行時,調用堆棧

運行在單線程上,使得運行首先。由於JavaScript只有一個調用堆棧,如果運行速度變慢,那應該怎麼解決呢?

併發和事件循環

想像一下如果調用堆棧裡面有些函數的執行需要大量的時間,例如複雜的圖片轉化,事情會怎麼花樣呢?

問題就是調用堆棧中的函數在執行的過程中,瀏覽器是不能做其它事情的,也就是會被調用堆棧中的函數阻塞,此時瀏覽器不能渲染和運行其它代碼,完全被卡住了。這樣就很難實現流暢的UI體驗。

另外一個問題也會由此發生,當瀏覽器在調用堆棧中執行很多這樣複雜且耗時的函數時,瀏覽器也會失去響應,出現假死狀態。

JavaScript工作原理:引擎,運行時,調用堆棧

怎麼才能執行復雜且耗時的代碼,並且不會阻塞UI的渲染和導致瀏覽器假死?解決方案就是異步調用


分享到:


相關文章: