你們在進行javascript開發的時候,有沒有想過,咱們寫的代碼是怎麼樣運行的呢?下面咱們就來剖析一下代碼的執行過程。javascript
代碼在運行過程當中,會有一個叫作調用棧(call stack)的概念。調用棧是一種棧結構,它用來存儲計算機程序執行時候其活躍子程序的信息。(好比什麼函數正在執行,什麼函數正在被這個函數調用等等信息)。調用棧是解析器的一種機制。call stackhtml
咱們以一段簡單代碼爲示例,來看一看到底什麼是調用棧,它是一個怎麼樣的運行機制java
function boo (a) { return a * 3 } function foo (b) { return boo(4) * 2 } console.log(foo(3))
下面咱們來分析一下上述代碼的執行過程
(1)console.log(foo(3)) 執行,造成一個棧幀,調用foo函數,再造成另外一個棧幀。
(2)新的棧幀壓在上一個棧幀之上,繼續執行代碼,foo函數中又調用了boo函數,造成了另外一個棧幀壓在舊棧幀之上。而後執行boo。
(3)當執行完boo時候,返回值給foo函數以後,boo被推出調用棧,foo函數繼續執行,而後foo函數執行完,返回值給console.log,foo函數被推出調用棧,console.log獲得foo函數的返回值,運行,輸出結果,最後console.log也被推出調用棧,該段程序執行完成。
圖解代碼運行過程:數據結構
// 省略一部分html <button>click</button> $.on('button', 'click', function onClick() { setTimeout(function timer() { console.log('You clicked the button!'); }, 0); }); console.log("Hi!"); setTimeout(function timeout() { console.log("Click the button!"); }, 5000); console.log("My Name Is Chirs.")
你們看看上敘的代碼,結合一下前面的的分析,思考一下調用棧是怎麼工做的?
(1)先運行綁定事件函數,把onClick事件綁定在button標籤上。該函數沒有沒有調用其餘函數。
(2)接下來運行console.log("hi"),該函數沒有調用任何其餘函數。
(3)而後繼續執行下面的setTimeout,setTimeout是一個異步函數,通過5秒以後,在運行隊列裏面插入這個回調函數,而後若是該隊列以前沒有其餘函數,就執行該隊列,有則等待前面的函數執行完成,再執行。
(4)console.log("My Name Is Chirs")不會等待5s以後,再執行,由於settimeout並不會在調用棧中執行5秒,實際上它在調用棧中是當即執行完的。
(5)假設在這個時候,咱們點擊了按鈕,按鈕綁定的回調事件被添加到運行隊列中。(運行隊列中的代碼要等調用棧被清空以後纔會執行)因爲調用棧中還有代碼須要執行,因此會繼續執行下面的console.log()
(6)而後執行完console.log以後,因爲時間尚未通過5s,因此點擊的回調事件會被先壓入棧中去執行,因爲該回調事件裏面又是一個settimeout事件,因爲它的事件間隔只有0s,因此這個settimeout的回調會先被壓入運行隊列。先輸出You clicked the button! 再過幾秒以後,間隔爲5s的settimeout把回調函數壓入隊列,這時候調用棧中沒有代碼在執行,因此會執行這個代碼,輸出"Click the button「。結束代碼運行。異步
一樣來看一個運行示意圖:函數
調用棧其實就是一種解析器去處理程序的機制,它是棧數據結構。它能追蹤子程序的運行狀態。
(1)當腳本要調用一個函數時,解析器把該函數添加到棧中而且執行這個函數。並造成一個棧幀
(2)任何被這個函數調用的函數會進一步添加到調用棧中,造成另外一個棧幀,而且運行到它們被上個程序調用的位置。
(3)當執行完這個函數後,若是它沒有調用其餘函數,則它會從調用棧中推出。而後調用棧繼續運行其餘部門。
(4) 異步函數的回調函數通常都會被添加到運行隊列裏面,如settimeout會在響應的時間後把回調函數放入隊列中,隊列裏的函數須要等棧爲空時纔會被推入棧中執行。若是隊列中有其餘函數,須要等隊列前面的函數被堆入調用棧中以後纔會運行。spa