摘要:當遇到函數(shù)調(diào)用時,引擎為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文并把它壓入當前執(zhí)行棧的頂部。參考鏈接理解中的執(zhí)行上下文和執(zhí)行棧深入之執(zhí)行上下文棧
開篇
作為一個JavaScript的程序開發(fā)者,如果被問到JavaScript代碼的執(zhí)行順序,你腦海中是不是有一個直觀的印象 -- JavaScript 是順序執(zhí)行的,可事實真的是這樣的嗎?
讓我們首先看兩個小例子:
var foo = function () { console.log("foo1"); } foo(); // foo1 var foo = function () { console.log("foo2"); } foo(); // foo2
function foo() { console.log("foo1"); } foo(); // foo2 function foo() { console.log("foo2"); } foo(); // foo2
刷過面試題目的都知道:
JavaScript引擎并非一行一行地分析和執(zhí)行程序,而是一段一段地分析執(zhí)行,當執(zhí)行一段代碼的時候,會進行一個準備工作。
比如我們熟悉的JavaScript中的變量提升比如函數(shù)提升都是在這個準備階段完成的。
本文我們就來深入的研究一下,這一段一段中的段是如何劃分的呢?
到底JavaScript引擎遇到一段怎樣的代碼才會做"準備工作"呢?為了解答這個問題我們引入一個概念——執(zhí)行上下文。
執(zhí)行上下文如果你做過小學的閱讀理解,肯定見到過這樣的題目:聯(lián)系上下文解釋句子,這里的上下文指的可能是這個句子所在的段落,也可能是這個句子所在段落的臨近段落。實際上,這里描述的是一個句子的語境和作用范圍,聯(lián)系類比到程序中我們可以作如下定義:
執(zhí)行上下文是當前JavaScript代碼被解析和執(zhí)行時所在環(huán)境的抽象概念。執(zhí)行上下文的類型
執(zhí)行上下文總共分為三種類型,有時候我們也叫做可執(zhí)行代碼(executable code)
全局執(zhí)行上下文: 只有一個,瀏覽器中的全局對象就是window對象,this指向這個全局對象。
函數(shù)執(zhí)行上下文: 存在無數(shù)個,只有在函數(shù)被調(diào)用的時候才會被創(chuàng)建,每次調(diào)用函數(shù)都會創(chuàng)建一個新的執(zhí)行上下文。
Eval 函數(shù)執(zhí)行上下文: 指的是運行在eval函數(shù)中的代碼,很少用而且不建議使用。
舉個例子,當執(zhí)行到一個函數(shù)的時候,就會進行準備工作,這里的"準備工作",讓我們用個更專業(yè)一點的說法,就叫做"執(zhí)行上下文(execution context)"。
執(zhí)行棧接下來問題來了,我們寫的函數(shù)多了去了,如何管理創(chuàng)建的那么多執(zhí)行上下文呢?所以 JavaScript 引擎創(chuàng)建了執(zhí)行上下文棧(Execution context stack )ECStack 來管理執(zhí)行上下文。
這里我們可以簡單的認為 ECStack 是一個數(shù)組,類似這樣:
ECStack = [];
執(zhí)行棧,也叫做調(diào)用棧,具有 LIFO(last in first out 后進先出) 結(jié)構(gòu),用于存儲在代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文。
首次運行JavaScript代碼的時候,會創(chuàng)建一個全局執(zhí)行的上下文并Push到當前的執(zhí)行棧中,每當發(fā)生函數(shù)調(diào)用,引擎都會為該函數(shù)創(chuàng)建一個新的函數(shù)執(zhí)行上下文并Push當前執(zhí)行棧的棧頂。
當棧頂?shù)暮瘮?shù)運行完成后,其對應(yīng)的函數(shù)執(zhí)行上下文將會從執(zhí)行棧中Pop出,上下文的控制權(quán)將移動到當前執(zhí)行棧的下一個執(zhí)行上下文。
讓我們看一段代碼來理解這個過程:
var a = "Hello World!"; function first() { console.log("Inside first function"); second(); console.log("Again inside first function"); } function second() { console.log("Inside second function"); } first(); console.log("Inside Global Execution Context"); // Inside first function // Inside second function // Again inside first function // Inside Global Execution Context
當上述代碼在瀏覽器加載時,JavaScript引擎創(chuàng)建了一個全局執(zhí)行上下文并把它壓入(push) 當前的執(zhí)行棧。當遇到 first() 函數(shù)調(diào)用時,JavaScript引擎為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文并把它壓入當前執(zhí)行棧的頂部。
當從 first() 函數(shù)內(nèi)部調(diào)用 second() 函數(shù)時,JavaScript引擎為 second() 函數(shù)創(chuàng)建了一個新的執(zhí)行上下文并把它壓入當前執(zhí)行棧的頂部,當 second() 函數(shù)執(zhí)行完畢,它的執(zhí)行上下文會從當前棧彈出(pop),并且控制流程到達下一個執(zhí)行上下文,即 first() 函數(shù)的執(zhí)行上下文。
當 first() 執(zhí)行完畢,它的執(zhí)行上下文從棧中彈出,控制流程到達了全局執(zhí)行上下文。一旦所有的代碼執(zhí)行完畢,JavaScript引擎從當前棧中移出全局執(zhí)行上下文。
下面這張圖,能夠更加清晰的解釋上面這個執(zhí)行過程
看兩個思考題var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope();
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()();
兩段代碼執(zhí)行的結(jié)果一樣,但是兩段代碼究竟有哪些不同呢?
答案就是執(zhí)行上下文棧的變化不一樣。
讓我們模擬第一段代碼:
ECStack.push(functionContext); ECStack.push( functionContext); ECStack.pop(); ECStack.pop();
讓我們模擬第二段代碼:
ECStack.push(functionContext); ECStack.pop(); ECStack.push( functionContext); ECStack.pop();
為了更詳細講解兩個函數(shù)執(zhí)行上的區(qū)別,我們需要探究一下執(zhí)行上下文到底包含了哪些內(nèi)容,我們需要更加深入了解變量對象的相關(guān)內(nèi)容。
參考鏈接:《理解 JavaScript 中的執(zhí)行上下文和執(zhí)行棧》
《JavaScript深入之執(zhí)行上下文?!?/p>
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/101738.html
摘要:思考題在深入學習之詞法作用域和動態(tài)作用域中,提出這樣一道思考題思考題一思考題二兩段代碼都會打印但是還是有些許差異的,本文就詳細的解析執(zhí)行上下文棧和執(zhí)行上下文的具體變化過程。 在《深入學習js之——執(zhí)行上下文?!分姓f過,當JavaScript代碼執(zhí)行一段可執(zhí)行代碼(executable code)時,會創(chuàng)建對應(yīng)的執(zhí)行上下文(execution context) 對于每一個執(zhí)行上下文,都有...
摘要:開篇作用域是每種計算機語言最重要的基礎(chǔ)之一,因此要想深入的學習作用域和作用域鏈就是個繞不開的話題。這樣由多個執(zhí)行上下文的變量對象構(gòu)成的鏈表就叫做作用域鏈。這時候執(zhí)行上下文的作用域鏈,我們命名為至此,作用域鏈創(chuàng)建完畢。 開篇 作用域是每種計算機語言最重要的基礎(chǔ)之一,因此要想深入的學習JavaScript,作用域和作用域鏈就是個繞不開的話題。 在《深入學習js之—-執(zhí)行上下文?!分形覀兲岬?..
摘要:本計劃一共期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃,點擊查看前端進階的破冰之旅本期推薦文章深入之執(zhí)行上下文棧和深入之變量對象,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。 (關(guān)注福利,關(guān)注本公眾號回復[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導) 本周正式開始前端進階的第一期,本周的主題是調(diào)用堆棧,今天是第二天。 本計劃一共28期,每期...
摘要:寫在前面深入系列共計篇已經(jīng)正式完結(jié),這是一個旨在幫助大家,其實也是幫助自己捋順底層知識的系列。深入系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 深入系列共計 15 篇已經(jīng)正式完結(jié),這是一個旨在幫助大家,其實也是幫助自己捋順 JavaScript 底層知識的系列。重點講解了如原型、作用域、執(zhí)行上下文、變量對象、this、...
摘要:深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的為例,具體講解當函數(shù)執(zhí)行的時候,執(zhí)行上下文棧變量對象作用域鏈是如何變化的。前言在深入之執(zhí)行上下文棧中講到,當代碼執(zhí)行一段可執(zhí)行代碼時,會創(chuàng)建對應(yīng)的執(zhí)行上下文。 JavaScript深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的demo為例,具體講解當函數(shù)執(zhí)行的時候,執(zhí)行上下文棧、變量對象、作用域鏈是如何變化的。 前言 在《Jav...
閱讀 1305·2021-11-23 09:51
閱讀 3421·2021-09-06 15:00
閱讀 995·2021-08-16 10:57
閱讀 1381·2019-08-30 12:46
閱讀 946·2019-08-29 12:22
閱讀 1614·2019-08-29 11:07
閱讀 3159·2019-08-26 11:23
閱讀 2993·2019-08-23 15:14