Javascript执行上下文和执行栈
什么是執(zhí)行上下文?
執(zhí)行上下文就是當(dāng)前JavaScript代碼被解析和執(zhí)行時所在環(huán)境的抽象概念,JavaScript中運行任何的代碼都是在執(zhí)行上下文。
什么是執(zhí)行棧?
執(zhí)行棧,在其他編程語言中也被叫做調(diào)用棧,具有LIFO(后進先出)結(jié)構(gòu),用于存儲在代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文。當(dāng)JavaScript引擎首次讀取腳本時,它會創(chuàng)建一個全局執(zhí)行上下文并將其推入當(dāng)前的執(zhí)行棧。每當(dāng)發(fā)生一個函數(shù)調(diào)用,引擎都會為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文,并將其推到當(dāng)前執(zhí)行棧的頂端。引擎會運行執(zhí)行上下文在執(zhí)行棧頂端的函數(shù),當(dāng)此函數(shù)運行完成后,其對應(yīng)的執(zhí)行上下文將會從執(zhí)行棧中彈出,上下文控制權(quán)將移到當(dāng)前執(zhí)行棧的下一個執(zhí)行上下文。所以程序結(jié)束以前,執(zhí)行棧最底部永遠有一個globalContext。
執(zhí)行上下文的類型
執(zhí)行上下文總共有三種類型:
一、全局執(zhí)行上下文:這是默認的,基礎(chǔ)的執(zhí)行上下文。不在任何函數(shù)中的代碼都位于全局執(zhí)行上下文中,做了兩件事:1.創(chuàng)建一個全局對象,在瀏覽器中這個全局對象就是window對象。2.將this指針指向這個全局對象。一個程序中只能存在一個全局執(zhí)行上下文。
二、函數(shù)執(zhí)行上下文:每次調(diào)用函數(shù)時,都會為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文。每個函數(shù)都擁有自己的執(zhí)行上下文,但是只有在函數(shù)被調(diào)用的時候才會被創(chuàng)建。一個程序中可以存在任意數(shù)量的函數(shù)執(zhí)行上下文。每當(dāng)一個新的執(zhí)行上下文被創(chuàng)建,它都會按照特定的順序執(zhí)行一系列步驟。
三、Eval函數(shù)執(zhí)行上下文:運行在eval函數(shù)中的代碼也獲得自己的執(zhí)行上下文,不常用函數(shù),不建議使用。
執(zhí)行上下文如何被創(chuàng)建?
執(zhí)行上下文分兩個階段創(chuàng)建:1)創(chuàng)建階段;2)執(zhí)行階段
一、創(chuàng)建階段:在任意的JavaScript代碼被執(zhí)行之前,執(zhí)行上下文處于創(chuàng)建階段。在創(chuàng)建階段中總共發(fā)生了三件事情:
1.確定this的值,也被稱為This Binding;
2.LexicalEnvironment(詞法環(huán)境)組件被創(chuàng)建。
3.VariableEnvironment(變量環(huán)境)組件被創(chuàng)建。
創(chuàng)建階段
Ⅰ、This Binding:
在全局執(zhí)行上下文中,this的值指向全局對象,在瀏覽器中,this的值指向window對象。在函數(shù)指向上下文中,this的值取決于函數(shù)的調(diào)用方式。如果它被一個對象引用調(diào)用,那么this的值被設(shè)置為該對象,否則this的值被設(shè)置為全局對象或undefined(嚴(yán)格模式下)。
Ⅱ、LexicalEnvironment(詞法環(huán)境):
ES6文檔將詞法環(huán)境定義為:詞法環(huán)境是一種規(guī)范類型,基于ECMAScript代碼的詞法嵌套結(jié)構(gòu)來定義標(biāo)識符與特定變量和函數(shù)的關(guān)聯(lián)關(guān)系(environment record)和可能為空引用(null)的外部詞法環(huán)境組成。簡而言之,詞法環(huán)境是一個包含標(biāo)識符變量映射的結(jié)構(gòu)。(這里的標(biāo)識符表示變量/函數(shù)的名稱,變量是對實際對象【包含函數(shù)類型對象】或原始值的引用)
在LexicalEnvironment(詞法環(huán)境)中,有兩個部分組成:(1)環(huán)境記錄(environment record):是存儲變量和函數(shù)聲明的實際位置 (2)對外部環(huán)境的引用:意味著它可以訪問外部詞法環(huán)境。
LexicalEnvironment(詞法環(huán)境)有兩種類型:
1.全局環(huán)境(在全局執(zhí)行上下文中)是一個沒有外部環(huán)境的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用為null。它擁有一個全局對象(window對象)及其關(guān)聯(lián)的方法和屬性(例如數(shù)組方法)以及任何用戶自定義的全局變量,this的值指向這個全局對象。
2.函數(shù)環(huán)境,用戶在函數(shù)中定義的變量被存儲在環(huán)境記錄中,對外部環(huán)境的引用可以是全局,也可以是包含內(nèi)部函數(shù)的外部函數(shù)環(huán)境。
注意:對于函數(shù)環(huán)境而言,環(huán)境記錄還包含了一個arguments對象,該對象包含了索引和傳遞給函數(shù)的參數(shù)之間的映射以及傳遞給函數(shù)的長度(數(shù)量)。
環(huán)境記錄同樣有兩種類型:
1.聲明性環(huán)境記錄:存儲變量、函數(shù)和參數(shù)。一個函數(shù)環(huán)境包含聲明性環(huán)境記錄。
2.對象環(huán)境記錄:用于定義在全局執(zhí)行上下文中出現(xiàn)的變量和函數(shù)的關(guān)聯(lián)。全局環(huán)境包含對象環(huán)境記錄。
Ⅲ、VariableEnvironmen(變量環(huán)境):
它也是一個詞法環(huán)境,其EnvironmentRecord包含了由VariableStatements在此執(zhí)行上下文創(chuàng)建的綁定。它具有上面定義的語法環(huán)境的所有屬性。與LexicalEnvironment的區(qū)別在于前者用于存儲函數(shù)聲明和變量(let和const)綁定,而后者僅用于存儲變量(var)綁定。
結(jié)合實例分析:
let a=10;const b=20;var c;function addFun (e,f){var g =50;return e*f*g;}c = addFun(30,40)/*GlobalExecutionContext = {ThisBinding = <Global Object> //確定thisLexicalEnvironment = {EnvironmentRecord:{Type:'Object' //全局環(huán)境//標(biāo)識符綁定a:<uninitialized>,b:<uninitialized>,addFun:<Func>},outer:<null> //對外部環(huán)境的引用}, //詞法環(huán)境VariableEnvironment = {EnvironmentRecord:{Type:'Object' //全局環(huán)境//標(biāo)識符綁定c:undefined},outer:<null> //對外部環(huán)境的引用} //變量環(huán)境}FunctionExecutionContext = {ThisBinding = <Global Object> //確定thisLexicalEnvironment = {EnvironmentRecord:{Type:'Declarative' //函數(shù)環(huán)境//標(biāo)識符綁定Arguments:{0:30,1:40,length:2}},outer:<GlobalLexicalEnvironment>//對外部環(huán)境的引用}, //詞法環(huán)境VariableEnvironment = {EnvironmentRecord:{Type:'Declarative' //函數(shù)環(huán)境//標(biāo)識符綁定g:undefined},outer:<GlobalLexicalEnvironment>//對外部環(huán)境的引用} //變量環(huán)境}*/只有在addFun調(diào)用的時候才會創(chuàng)建函數(shù)執(zhí)行上下文。創(chuàng)建之初,代碼會被掃描并解析變量和函數(shù)聲明,其中函數(shù)聲明存儲在環(huán)境之中,而變量會被設(shè)置為undefined(在var情況下)或保持未初始化(在let和const情況下)。這也是為什么可以在聲明之前訪問var定義的變量(盡管是undefined),但如果在let或const之前訪問定義的變量會提示引用錯誤的原因。
這就是所謂的變量提升。
函數(shù)上下文
在函數(shù)上下文中,用活動對象(activation object,AO)來表示變量對象。
活動對象和變量對象的區(qū)別在于:
1.變量對象(VO)是規(guī)范上或者是JS引擎上實現(xiàn)的,并不能在JS環(huán)境中直接訪問。
2.當(dāng)進入到一個執(zhí)行上下文后,這個變量才會被激活,所以叫活動變量(AO),這個時候活動變量上的各種屬性才能被訪問。
調(diào)用函數(shù)的時候,會為其創(chuàng)建一個Arguments對象,并自動初始化局部變量arguments,指代該Arguments對象。所有作為參數(shù)傳入的值都會成為Arguments對象的數(shù)組元素。
二、執(zhí)行階段分為兩個階段。注:在執(zhí)行階段,如果JavaScript引擎在源代碼中聲明的位置找不到let變量的值,那么將為其分配undefined的值。
Ⅰ、進入執(zhí)行階段
此時的變量對象會進行初始化:
1.函數(shù)的所有形參:沒有實參,屬性值設(shè)為undefined。
2.函數(shù)聲明:如果變量對象已經(jīng)存在相同的屬性,則完全替換這個屬性。
3.變量聲明:如果變量名稱跟已經(jīng)聲明的形參或函數(shù)相同,則變量聲明不會干擾已經(jīng)存在的屬性。
Ⅱ、代碼執(zhí)行
這個階段會順序執(zhí)行代碼,修改變量對象的值。
執(zhí)行階段總結(jié):
1.全局上下文的變量初始化是全局對象;
2.函數(shù)上下文的變量對象初始化只包括Arguments對象;
3.在進入執(zhí)行上下文時會給變量對象添加形參,函數(shù)聲明,變量聲明等初始屬性值
4.在代碼執(zhí)行階段,會再次修改變量對象的屬性值。
轉(zhuǎn)載于:https://www.cnblogs.com/xuxiaoqiangAndHM/p/10514247.html
總結(jié)
以上是生活随笔為你收集整理的Javascript执行上下文和执行栈的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C++】满二叉树问题
- 下一篇: POJ 1258 Agri-Net (最