圖解JS執(zhí)行上下文
當(dāng)前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
執(zhí)行上下文是什么?執(zhí)行上下文是一個抽象的概念,用于描述代碼執(zhí)行時的環(huán)境,包含了當(dāng)前代碼運行時所需的變量、函數(shù)和參數(shù)。 從一段JS的執(zhí)行談起單看概念可能還有點懵,其實執(zhí)行上下文“誕生”的時機是JS代碼編譯的時候,“被操作”的時機是JS代碼執(zhí)行的時候。 什么樣的JS代碼會被編譯產(chǎn)生執(zhí)行上下文呢?一般來說,有三種情況:
以這段代碼為例:
這段代碼很簡單,先是創(chuàng)建了一個 add 函數(shù),接著在代碼的最下面又調(diào)用了該函數(shù)。 那么下面我們就利用圖片來解釋下函數(shù)調(diào)用的過程。 在執(zhí)行到函數(shù) add() 之前,JavaScript 引擎會為上面這段代碼創(chuàng)建全局執(zhí)行上下文,包含了聲明的函數(shù)和變量,你可以參考下圖: 從圖中可以看出,代碼中全局變量和函數(shù)都保存在全局上下文的變量環(huán)境中。 執(zhí)行上下文準(zhǔn)備好之后,便開始執(zhí)行全局代碼,當(dāng)執(zhí)行到 add 這兒時,JavaScript 判斷這是一個函數(shù)調(diào)用,那么將執(zhí)行以下操作:
完整流程你可以參考下圖: 就這樣,當(dāng)執(zhí)行到 add 函數(shù)的時候,我們就有了兩個執(zhí)行上下文了——全局執(zhí)行上下文和 add 函數(shù)的執(zhí)行上下文。 也就是說在執(zhí)行 JavaScript 時,可能會存在多個執(zhí)行上下文,那么 JavaScript 引擎是如何管理這些執(zhí)行上下文的呢? 答案是通過一種叫棧的數(shù)據(jù)結(jié)構(gòu)來管理的。那什么是棧呢?它又是如何管理這些執(zhí)行上下文呢? 什么是棧關(guān)于棧,你可以結(jié)合這么一個貼切的例子來理解,一條單車道的單行線,一端被堵住了,而另一端入口處沒有任何提示信息,堵住之后就只能后進去的車子先出來,這時這個堵住的單行線就可以被看作是一個棧容器,車子開進單行線的操作叫做入棧,車子倒出去的操作叫做出棧。 在車流量較大的場景中,就會發(fā)生反復(fù)的入棧、棧滿、出棧、空棧和再次入棧,一直循環(huán)。 所以,棧就是類似于一端被堵住的單行線,車子類似于棧中的元素,棧中的元素滿足后進先出的特點。 你可以參考下圖: 什么是 JavaScript 的調(diào)用棧JavaScript 引擎正是利用棧的這種結(jié)構(gòu)來管理執(zhí)行上下文的。在執(zhí)行上下文創(chuàng)建好后,JavaScript 引擎會將執(zhí)行上下文壓入棧中,通常把這種用來管理執(zhí)行上下文的棧稱為執(zhí)行上下文棧,又稱調(diào)用棧。 為便于你更好地理解調(diào)用棧,下面我們再來看段稍微復(fù)雜點的示例代碼:
在上面這段代碼中,你可以看到它是在 addAll 函數(shù)中調(diào)用了 add 函數(shù),那在整個代碼的執(zhí)行過程中,調(diào)用棧是怎么變化的呢? 下面我們就一步步地分析在代碼的執(zhí)行過程中,調(diào)用棧的狀態(tài)變化情況。 第一步,創(chuàng)建全局上下文,并將其壓入棧底。如下圖所示:
從圖中你也可以看出,變量 a、函數(shù) add 和 addAll 都保存到了全局上下文的變量環(huán)境對象中。全局執(zhí)行上下文壓入到調(diào)用棧后,JavaScript 引擎便開始執(zhí)行全局代碼了。首先會執(zhí)行 a=2 的賦值操作,執(zhí)行該語句會將全局上下文變量環(huán)境中 a 的值設(shè)置為 2。設(shè)置后的全局上下文的狀態(tài)如下圖所示:
接下來,第二步是調(diào)用 addAll 函數(shù)。當(dāng)調(diào)用該函數(shù)時,JavaScript 引擎會編譯該函數(shù),并為其創(chuàng)建一個執(zhí)行上下文,最后還將該函數(shù)的執(zhí)行上下文壓入棧中,如下圖所示: addAll 函數(shù)的執(zhí)行上下文創(chuàng)建好之后,便進入了函數(shù)代碼的執(zhí)行階段了,這里先執(zhí)行的是 d=10 的賦值操作,執(zhí)行語句會將 addAll 函數(shù)執(zhí)行上下文中的 d 由 undefined 變成了 10。 然后接著往下執(zhí)行,第三步,當(dāng)執(zhí)行到 add 函數(shù)調(diào)用語句時,同樣會為其創(chuàng)建執(zhí)行上下文,并將其壓入調(diào)用棧,如下圖所示:
當(dāng) add 函數(shù)返回時,該函數(shù)的執(zhí)行上下文就會從棧頂彈出,并將 result 的值設(shè)置為 add 函數(shù)的返回值,也就是 9。如下圖所示:
緊接著 addAll 執(zhí)行最后一個相加操作后并返回,addAll 的執(zhí)行上下文也會從棧頂部彈出,此時調(diào)用棧中就只剩下全局上下文了。最終如下圖所示:
至此,整個 JavaScript 流程執(zhí)行結(jié)束了。 總結(jié)
參考資料:《瀏覽器工作原理與實踐》
該文章在 2024/7/24 16:36:34 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |