欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入學習JavaScript執(zhí)行上下文

 更新時間:2022年08月08日 09:19:12   作者:尤可愛???????  
這篇文章主要介紹了深入學習JavaScript執(zhí)行上下文,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,需要的朋友可以參考一下,希望對你的學習有所幫助

前言

我們先不看這個標題,來看下面這段代碼是怎么運行的:

var num1=1
var num1=2
var result=num1+num2
console.log(result)

我相信,小伙伴都知道打印的是3把,這也太簡單了,但是我們思考下,這段代碼執(zhí)行時,到底進行了怎樣的操作,這里我們要引進一個概念:初始化全局對象和執(zhí)行上下文

初始化全局對象(GO)

JS引擎在解析代碼的時候,會在堆內存中創(chuàng)建一個全局對象叫:Gobal Object(GO)

  • 該對象所有的作用域都可以訪問
  • 里面會包含Date Array String Number setTimeout setInterval 等等
  • 還有一個windows屬性指向本身

執(zhí)行上下文

代碼運行時:js引擎(這里考慮V8引擎)會創(chuàng)建執(zhí)行上下文棧(Execution context stack)簡稱ECS,它就是執(zhí)行代碼的調用棧

執(zhí)行上下文分為:

  • 全局執(zhí)行上下文( GEC )
  • 函數(shù)執(zhí)行上下文( FEC )

這里我們先分析下全局執(zhí)行上下文,因為我們開頭那段代碼是全局代碼

全局執(zhí)行上下文

在執(zhí)行全局代碼的時候,會創(chuàng)建全局執(zhí)行上下文( GEC ),GEC會被放入ECS中去執(zhí)行 GEC被放入ECS

包含兩部分:

  • 第一部分:在代碼執(zhí)行前,也就是V8引擎將parser轉成AST的過程中 ( 這里會涉及到V8引擎在解析js代碼的過程,會開篇專題講這個),會將全局定義的變量放入GO中,但是并不會賦值,因為是解析階段 (pass:這個過程也被叫做變量的作用域提升
  • 第二部分:執(zhí)行代碼,并為GO里面的變量賦值,或者執(zhí)行其他函數(shù) 流程圖如下:圖中執(zhí)行前是上面描述的第一部分,開始執(zhí)行代碼是第二部分

Java Script遇到函數(shù)代碼如何執(zhí)行?

當js引擎遇到函數(shù)執(zhí)行時,會創(chuàng)建一個 函數(shù)執(zhí)行上下文( Functional Execution Contex ) 簡稱FEC,并壓入到執(zhí)行上下文棧ECS

FEC中包含三部分內容:

  • 第一部分:VO(variable object ) 對象,其實也是AO( Activation Object )
  • 第二部分:作用域鏈(scope chain):由自己的VO+父級的VO,查找時會一層一層去查找
  • 第三部分:this綁定( 這里不做詳細描述,后續(xù)會出專題 )

作用域是解析編譯的時候就決定了,并不是執(zhí)行調用的時候來我們看一段代碼,一起看一下函數(shù)的執(zhí)行過程,一起來思考下這段代碼的運行結果

var message = "Global"
function foo() {
  console.log(message)
}
function bar() {
  var message = "Bar"
  foo()
}
bar()

畫個圖分析一下吧

首先初始化全局對象GO,里面有Array、date、setTime等等,還有自己定義的 message對象初始值為undefined,foo和bar函數(shù)對象

開始執(zhí)行代碼前,會創(chuàng)建一個執(zhí)行上下文棧ESC,開始執(zhí)行全局代碼,所以會創(chuàng)建一個全局執(zhí)行上下文棧GEC,將GEC壓入棧底,全局執(zhí)行上下文包括兩部分:
第一部分代碼執(zhí)行前的VO對象(這里的VO指向的是GO)
第二部分是開始執(zhí)行代碼,執(zhí)行第一行時 var message="Global",GO對象里的message就被改為Global 

執(zhí)行完第一行后,開始執(zhí)行第9行代碼 bar(),這里是函數(shù)的調用執(zhí)行,js引擎會創(chuàng)建一個函數(shù)執(zhí)行上下文FEC,壓入棧中,FEC包含三部分:

第一部分:VO對象,這里指向bar自己的AO對象(包括形參和函數(shù)中定義的變量)
第二部分:作用域鏈scope chain,自己的VO對象+parent VO對象

第三部分:this綁定,這里是指向windows(這個后續(xù)會開專題講)
開始執(zhí)行bar函數(shù)里面的代碼,var message="Bar",會將bar的AO對象的message變量值從undefined改為"Bar",接下來在執(zhí)行foo()函數(shù),注意:此時bar函數(shù)還沒彈出棧,因為foo函數(shù)還在執(zhí)行

訪問一個變量的時候,會沿著作用域鏈一層一層往上找,最后沒有找到則會報錯

執(zhí)行到bar函數(shù)的最后一行代碼是foo(),此時又是一個函數(shù)的調用執(zhí)行,又會創(chuàng)建一個foo的函數(shù)執(zhí)行上下文,也包含上述的三部分:VO對象(指向foo的AO對象) 作用域鏈 和this綁定
開始執(zhí)行foo函數(shù)代碼console.log(message),當打印message的時候,會沿著作用域鏈一層層找,foo的作用域鏈是自己的AO+父級的VO(也就是GO對象),自己的AO對象為空,所以找到GO里面的message變量,最終打印"Global" 

執(zhí)行完后,函數(shù)執(zhí)行上下文執(zhí)行完之后,就會彈出棧,foo的FEC先彈出棧,然后bar的FEC彈出棧,他們自個的AO對象最終也被釋放

環(huán)境變量和記錄

我們上述所說的VO是基于早期的ECMAS規(guī)范,官網(wǎng)是這樣說的:

 這里借助coderwhy的翻譯:每個執(zhí)行上下文都關聯(lián)一個變量對象(Vriable Object),在源代碼中變量和函數(shù)的聲明會被作為屬性加入到變量對象中,對于函數(shù)來說,參數(shù)也會被加入到VO中但是ECMA5以后的版本,官網(wǎng)做了一些詞匯用語的修改 

每個執(zhí)行上下文都會關聯(lián)一個變量環(huán)境(variable Environment),在執(zhí)行代碼中變量和函數(shù)的聲明會被當做環(huán)境記錄(Environment Record)加入到變量環(huán)境中。
對于函數(shù)代碼,形參也會被當做環(huán)境記錄加入到變量環(huán)境中

總結:

  • 在訪問一個變量時,會沿著作用域鏈一層層往上找,最終沒找到,會報錯:未定義
  • 作用域以及父級作用域是代碼在編譯階段就已經(jīng)確定了,和調用位置沒關系

匯總一些名詞解釋,我們來解釋一下:

名詞解釋
ECS執(zhí)行上下文棧 (Execution Context Stack),也可稱為調用棧
GEC全局執(zhí)行上下文(Global Execution Context),在執(zhí)行全局代碼前創(chuàng)建
FEC函數(shù)執(zhí)行上下文(Functional Execution Context),在執(zhí)行函數(shù)前創(chuàng)建
VOVariable Object,早期ECMA規(guī)范中的變量環(huán)境,對應Object
VEVariable Environment,最新ECMA規(guī)范中的變量環(huán)境,對應環(huán)境記錄
GO全局對象(Global Object),解析全局代碼時創(chuàng)建,GEC中關聯(lián)的VO就是GO
AO活動對象(Activation Object),VO被激活就變成了AO,VO和AO本質上是一個東西,它們其實都是同一個對象,只是處于執(zhí)行環(huán)境的不同生命周期

到此這篇關于深入學習JavaScript執(zhí)行上下文的文章就介紹到這了,更多相關JS執(zhí)行上下文內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論