有了上一篇文章的基礎,今天咱們接着來學習 javascript 調用棧。
先來回顧下前面文章的內容:
三種 JavaScript 的可執行代碼(executable code):javascript
請記住這三種建立執行上下文的狀況,接下來咱們來看看調用棧。講清楚調用棧以前須要先弄明白函數調用和棧結構。java
函數調用就是運行一個函數,咱們結合下面的代碼說明:segmentfault
var a = 2 function add(){ var b = 10 return a+b } add()
在執行到函數 add() 以前,JavaScript 引擎會爲上面這段代碼建立全局執行上下文(全局代碼產生執行上下文),包含了聲明的函數和變量,你能夠參考下圖:
閉包
從圖中能夠看出,代碼中全局變量和函數都保存在全局上下文的變量環境中。函數
執行上下文準備好以後,便開始執行全局代碼,當執行到 add 這裏,JavaScript 判斷這是一個函數調用(調用函數產生執行上下文),那麼將執行如下操做:學習
完整流程參考下圖:網站
就這樣,當執行到 add 函數的時候,咱們就有了兩個執行上下文了——全局執行上下文和 add 函數的執行上下文。spa
也就是說在執行 JavaScript 時,可能會存在多個執行上下文,那麼 JavaScript 引擎是如何管理這些執行上下文的呢?3d
答案就是咱們今天的主題 —— 經過調用棧管理code
JavaScript 引擎利用棧的結構來管理執行上下文。在執行上下文建立好後,JavaScript 引擎會將執行上下文壓入棧中,一般把這種用來管理執行上下文的棧稱爲執行上下文棧,又稱調用棧。
下面咱們再來看段稍微複雜點的示例代碼:
var a = 2 function add(b,c){ return b+c } function addAll(b,c){ var d = 10 var result = add(b,c) return a+result+d } addAll(3,6)
在上面這段代碼中,你能夠看到它是在 addAll 函數中調用了 add 函數,那在整個代碼的執行過程當中,調用棧是怎麼變化的呢?
下面咱們就一步步地分析在代碼的執行過程當中,調用棧的狀態變化狀況。
第一步,建立全局上下文,並將其壓入棧底。以下圖所示:
從圖中咱們能夠看出(編譯階段),變量 a、函數 add 和 addAll 都保存到了全局上下文的變量環境對象中。
全局執行上下文壓入到調用棧後,JavaScript 引擎開始執行全局代碼。首先會執行 a=2 的賦值操做,執行該語句會將全局上下文變量環境中 a 的值設置爲 2。設置後的全局上下文的狀態以下圖所示:
接下來,第二步是調用 addAll 函數。當調用該函數時,JavaScript 引擎會編譯該函數,併爲其建立一個執行上下文,最後還將該函數的執行上下文壓入棧中,以下圖所示:
addAll 函數的執行上下文建立好以後,便進入了函數代碼的執行階段了,這裏先執行的是 d=10 的賦值操做,執行語句會將 addAll 函數執行上下文中的 d 由 undefined 變成了 10。
而後接着往下執行,第三步,當執行到 add 函數調用語句時,一樣會爲其建立執行上下文,並將其壓入調用棧,以下圖所示:
當 add 函數返回時,該函數的執行上下文就會從棧頂彈出,並將 result 的值設置爲 add 函數的返回值,也就是 9。以下圖所示:
緊接着 addAll 執行最後一個相加操做後並返回,addAll 的執行上下文也會從棧頂部彈出,此時調用棧中就只剩下全局上下文了。最終以下圖所示:
至此,整個 JavaScript 流程執行結束了。
這裏強烈推薦一個國外小哥的網站,能夠很是清楚的展現調用棧的入棧和出棧過程,還能夠有閉包的過程哦(https://tylermcginnis.com/jav...)
好了,今天關於 javascript 調用棧的內容就結束了,下一篇文章咱們來說解一下let和const