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