日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

深入理解JavaScript執行上下文、函數堆棧、提升的概念

瀏覽:21日期:2023-11-14 10:41:01

首先明確幾個概念:

EC :函數執行環境(或執行上下文),Execution Context

ECS :執行環境棧,Execution Context Stack

VO :變量對象,Variable Object

AO :活動對象,Active Object

scope chain :作用域鏈

想當初自己看到這幾個概念的時候是一(m)臉(d)懵(z)逼(z),但是不得不說這幾個概念對以后深入學習JS有很大的幫助。來不及解釋了,趕緊上車~

EC(執行上下文)

每次當控制器轉到ECMAScript可執行代碼的時候,就會進入到一個執行上下文。

那什么是可執行代碼呢?

可執行代碼的類型

全局代碼( Global code )

這種類型的代碼是在'程序'級處理的:例如加載外部的js文件或者本地 <script></script> 標簽內的代碼。 全局代碼不包括任何function體內的代碼 。 這個是默認的代碼運行環境,一旦代碼被載入,引擎最先進入的就是這個環境。

函數代碼( Function code )

任何一個函數體內的代碼,但是需要注意的是, 具體的函數體內的代碼是不包括內部函數的代碼

Eval代碼( Eval code )

eval內部的代碼

這里僅僅引入EC這個概念,后面還有關于EC建立細節的介紹。

ECS(執行環境棧)

我們用MDN上的一個例子來引入函數執行棧的概念

function foo(i) { if (i < 0) return; console.log(’begin:’ + i); foo(i - 1); console.log(’end:’ + i);}foo(2);// 輸出:// begin:2// begin:1// begin:0// end:0// end:1// end:2

這里先不關心執行結果。磨刀不誤砍柴功,先了解一下函數執行上下文堆棧的概念。相信弄明白了下面的概念,一切也就水落石出了

我們都知道,瀏覽器中的JS解釋器被實現為單線程,這也就意味著同一時間只能發生一件事情,其他的行為或事件將會被放在叫做執行棧里面排隊。下面的圖是單線程棧的抽象視圖:

深入理解JavaScript執行上下文、函數堆棧、提升的概念

當瀏覽器首次載入你的腳本,它將 默認進入全局執行上下文 。如果,你在你的全局代碼中調用一個函數,你程序的時序將進入被調用的函數,并創建一個新的執行上下文,并將新創建的上下文壓入執行棧的頂部。

如果你調用當前函數內部的其他函數,相同的事情會在此上演。 代碼的執行流程進入內部函數,創建一個新的執行上下文并把它壓入執行棧的頂部。瀏覽器總會執行位于棧頂的執行上下文,一旦當前上下文函數執行結束,它將被從棧頂彈出,并將上下文控制權交給當前的棧 。這樣,堆棧中的上下文就會被依次執行并且彈出堆棧,直到回到全局的上下文。

看到這里,想必大家都已經深諳上述例子輸出結果的原因了,這里我大概繪了一個流程圖來幫助理解。

深入理解JavaScript執行上下文、函數堆棧、提升的概念

VO(變量對象)/AO(活動對象)

這里為什么要用一個 / 呢?按照字面理解,AO其實就是被激活的VO,兩個其實是一個東西。下面引用知乎上的一段話,幫助理解一下。 原文鏈接

變量對象 (Variable object) 是說JS的執行上下文中都有個對象用來存放執行上下文中可被訪問但是不能被 delete 的 函數標示符 、 形參 、 變量聲明 等。它們會被掛在這個對象上,對象的屬性對應它們的名字對象屬性的值對應它們的值但這個對象是規范上或者說是引擎實現上的不可在JS環境中訪問到活動對象

激活對象 (Activation object) 有了變量對象存每個上下文中的東西,但是它什么時候能被訪問到呢?就是每進入一個執行上下文時,這個執行上下文兒中的變量對象就被激活,也就是該上下文中的函數標示符、形參、變量聲明等就可以被訪問到了

EC 建立的細節

1、創建階段【當函數被調用,但未執行任何其內部代碼之前】

創建作用域鏈(Scope Chain)

創建變量,函數和參數。

求”this“的值

2、執行階段

初始化變量的值和函數的引用,解釋/執行代碼。

我們可以將每個執行上下文抽象為一個對象,這個對象具有三個屬性

ECObj: { scopeChain: { /* 變量對象(variableObject)+ 所有父級執行上下文的變量對象*/ }, variableObject: { /*函數 arguments/參數,內部變量和函數聲明 */ }, this: {} }

解釋器執行代碼的偽邏輯

1、查找調用函數的代碼。

2、執行代碼之前,先進入創建上下文階段:

初始化作用域鏈

創建變量對象:

創建arguments對象,檢查上下文,初始化參數名稱和值并創建引用的復制。

掃描上下文的函數聲明(而非函數表達式):

為發現的每一個函數,在變量對象上創建一個屬性——確切的說是函數的名字——其有一個指向函數在內存中的引用。

如果函數的名字已經存在,引用指針將被重寫。

掃描上下文的變量聲明:

為發現的每個變量聲明,在變量對象上創建一個屬性——就是變量的名字,并且將變量的值初始化為undefined

如果變量的名字已經在變量對象里存在,將不會進行任何操作并繼續掃描。

求出上下文內部“this”的值。

3、激活/代碼執行階段:

在當前上下文上運行/解釋函數代碼,并隨著代碼一行行執行指派變量的值。

VO --- 對應上述第二個階段

function foo(i){ var a = ’hello’ var b = function(){} function c(){}}foo(22)

當我們調用 foo(22) 時,整個創建階段是下面這樣的

ECObj = {scopChain: {...}, variableObject: { arguments: {0: 22,length: 1 }, i: 22, c: pointer to function c() a: undefined, b: undefined},this: { ... } }

正如我們看到的,在上下文創建階段,VO的初始化過程如下( 該過程是有先后順序的:函數的形參==>>函數聲明==>>變量聲明 ):

函數的形參(當進入函數執行上下文時) —— 變量對象的一個屬性,其屬性名就是形參的名字,其值就是實參的值;對于沒有傳遞的參數,其值為undefined

函數聲明(FunctionDeclaration, FD) —— 變量對象的一個屬性,其屬性名和值都是函數對象創建出來的; 如果變量對象已經包含了相同名字的屬性,則替換它的值

變量聲明(var,VariableDeclaration) —— 變量對象的一個屬性,其屬性名即為變量名,其值為undefined; 如果變量名和已經聲明的函數名或者函數的參數名相同,則不會影響已經存在的屬性。

對于函數的形參沒有什么可說的,主要看一下函數的聲明以及變量的聲明兩個部分。

1、如何理解函數聲明過程中 如果變量對象已經包含了相同名字的屬性,則替換它的值 這句話?

看如下這段代碼:

function foo1(a){ console.log(a) function a(){} }foo1(20)//’function a(){}’

根據上面的介紹,我們知道VO創建過程中,函數形參的優先級是高于函數的聲明的,結果是函數體內部聲明的 function a(){} 覆蓋了函數形參 a 的聲明,因此最后輸出 a 是一個 function

2、如何理解變量聲明過程中 如果變量名和已經聲明的函數名或者函數的參數名相同,則不會影響已經存在的屬性 這句話?

//情景一:與參數名相同function foo2(a){ console.log(a) var a = 10}foo2(20) //’20’//情景二:與函數名相同function foo2(){ console.log(a) var a = 10 function a(){}}foo2() //’function a(){}’

下面是幾個比較有趣的例子,當做加餐小菜,大家細細品味。這里給出一句話當做參考:

函數的聲明比變量優先級要高,并且定義過程不會被變量覆蓋,除非是賦值

function foo3(a){ var a = 10 function a(){} console.log(a)}foo3(20) //’10’function foo3(a){ var a function a(){} console.log(a)}foo3(20) //’function a(){}’

AO --- 對應第三個階段

正如我們看到的,創建的過程僅負責處理定義屬性的名字,而并不為他們指派具體的值,當然還有對形參/實參的處理。一旦創建階段完成,執行流進入函數并且激活/代碼執行階段,看下函數執行完成后的樣子:

ECObj = { scopeChain: { ... }, variableObject: {arguments: { 0: 22, length: 1},i: 22,c: pointer to function c()a: ’hello’,b: pointer to function privateB() }, this: { ... }} 提升(Hoisting)

對于下面的代碼,相信很多人都能一眼看出輸出結果,但是卻很少有人能給出為什么會產生這種輸出結果的解釋。

(function() { console.log(typeof foo); // 函數指針 console.log(typeof bar); // undefined var foo = ’hello’,bar = function() { return ’world’;}; function foo() {return ’hello’; }}());

1、為什么我們能在foo聲明之前訪問它?

回想在 VO 的創建階段,我們知道函數在該階段就已經被創建在變量對象中。所以在函數開始執行之前,foo已經被定義了。

2、Foo被聲明了兩次,為什么foo顯示為函數而不是undefined或字符串?

我們知道,在創建階段,函數聲明是優先于變量被創建的。而且在變量的創建過程中,如果發現 VO 中已經存在相同名稱的屬性,則不會影響已經存在的屬性。

因此,對 foo() 函數的引用首先被創建在活動對象里,并且當我們解釋到var foo時,我們看見 foo 屬性名已經存在,所以代碼什么都不做并繼續執行。

3、為什么bar的值是undefined?

bar 采用的是函數表達式的方式來定義的,所以 bar 實際上是一個變量,但變量的值是函數,并且我們知道變量在創建階段被創建但他們被初始化為 undefined ,這也是為什么函數表達式不會被提升的原因。

總結:

1、 EC 分為兩個階段,創建執行上下文和執行代碼。

2、每個 EC 可以抽象為一個對象,這個對象具有三個屬性,分別為:作用域鏈 Scope , VO|AO ( AO , VO 只能有一個)以及 this 。

3、函數 EC 中的 AO 在進入函數 EC 時,確定了Arguments對象的屬性;在執行函數 EC 時,其它變量屬性具體化。

4、 EC 創建的過程是由先后順序的:參數聲明 > 函數聲明 > 變量聲明

參考

javascript 執行環境,變量對象,作用域鏈

What is the Execution Context & Stack in JavaScript?

函數MDN

來自:https://segmentfault.com/a/1190000009041008

標簽: JavaScript
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲综合电影| 国产精品观看| 狠狠久久伊人中文字幕| 国产欧美日韩一级| 国产精品麻豆成人av电影艾秋| 日本久久一区| 九九99久久精品在免费线bt| 国产精品久久久久久久久久10秀| 激情黄产视频在线免费观看| 91精品xxx在线观看| 婷婷亚洲综合| 日韩中文字幕麻豆| 久久激情五月婷婷| 欧美激情网址| 肉色欧美久久久久久久免费看| 久久精品123| 亚洲欧洲一区| 日韩国产欧美三级| 另类综合日韩欧美亚洲| 91一区二区三区四区| 蜜臀久久99精品久久一区二区| 丝袜亚洲精品中文字幕一区| 日韩二区三区四区| 天堂а√在线最新版中文在线| 亚洲一二av| 国产精品久久久久9999高清| av在线最新| 久热精品在线| 国产一区二区三区亚洲| 亚洲在线网站| 国产一区三区在线播放| 久久不射中文字幕| 国产精品天堂蜜av在线播放| 久久国产电影| 国产亚洲字幕| 日韩视频中文| 精品视频网站| 亚洲一区有码| 日本少妇一区| 欧美日韩在线精品一区二区三区激情综合| 午夜av不卡| 欧美日韩一区二区国产| 亚洲国产专区| 免费在线播放第一区高清av| 亚洲激情av| 精品国产aⅴ| 亚洲人成精品久久久| 精品少妇一区| 日韩国产欧美一区二区三区| 免费成人网www| 久久久久久自在自线| 日韩国产在线不卡视频| 午夜日韩在线| 日本久久黄色| 国产精品亚洲成在人线| 视频一区视频二区中文| 日韩精品欧美| 国产a久久精品一区二区三区| 亚欧洲精品视频在线观看| 亚洲一级高清| а√天堂8资源中文在线| 日韩精品免费观看视频| 婷婷激情一区| 狠狠久久伊人| 久久gogo国模啪啪裸体| 婷婷五月色综合香五月| 亚洲一区二区免费看| 欧洲av不卡| 国产理论在线| 精品国产一区二区三区性色av| 91久久精品无嫩草影院| 老司机精品久久| 伊人久久大香线蕉av不卡| 国产精品99一区二区三区| 国产精品亚洲四区在线观看| 日本免费一区二区视频| 日韩一区精品| 自拍日韩欧美| 婷婷综合激情| 一区二区三区四区在线看| 成人久久一区| 久久精品在线| 日韩不卡免费高清视频| 中文在线а√天堂| av免费不卡国产观看| 精品视频91| 成人精品动漫一区二区三区| 麻豆视频一区二区| 加勒比视频一区| 日韩成人精品一区二区| 日韩精品视频在线看| 日韩精品a在线观看91| 在线免费观看亚洲| 亚洲一区二区三区无吗| 在线日韩成人| 天堂av一区| 日本中文字幕不卡| 日韩欧美高清一区二区三区| 日韩精品一卡二卡三卡四卡无卡 | 老色鬼久久亚洲一区二区| 免播放器亚洲| 亚洲精品极品| 国产欧美日韩一级| 国产精品115| 精品一区二区三区视频在线播放 | 国产精品videossex久久发布 | 国产图片一区| 另类欧美日韩国产在线| 国产精品成久久久久| 福利一区和二区| 日韩国产网站| 六月婷婷一区| 国产三级一区| 欧美激情另类| 不卡在线一区| 日本中文字幕视频一区| 久久影院一区二区三区| 国产精品不卡| 亚洲激情黄色| 日韩午夜视频在线| 久久精品国产网站| 国产h片在线观看| 亚洲精品小说| 日本免费新一区视频| 国产精品地址| 日韩精品首页| 中文字幕日韩欧美精品高清在线| 亚洲欧美专区| 精品三级在线| 妖精视频成人观看www| 日韩av影院| 国产夫妻在线| 亚洲综合日本| 国产精品v亚洲精品v日韩精品| 色婷婷色综合| 成人片免费看| 热久久免费视频| 久久丁香四色| 国产一区亚洲| 青青伊人久久| 成人日韩在线观看| 日韩av中文在线观看| 老牛影视精品| 午夜国产欧美理论在线播放| 国产999精品在线观看| 日韩视频精品在线观看| 国产精品久久久久77777丨| 成人看片网站| 日韩一区免费| 久久国产影院| 国产精品久久久久久久久久白浆| 九九久久婷婷| 国产日韩欧美一区在线| 在线综合亚洲| 黄色网一区二区| 日韩精品五月天| 婷婷成人在线| 精品视频一区二区三区在线观看| 丝瓜av网站精品一区二区 | 91精品麻豆| 久久在线视频免费观看| 日本不卡视频在线观看| 亚洲精品小说| 色偷偷色偷偷色偷偷在线视频| 日韩精品视频一区二区三区| 99久久99久久精品国产片果冰| 国产日韩免费| 首页欧美精品中文字幕| 中文字幕在线免费观看视频| 日本色综合中文字幕| 黄色亚洲在线| 黑森林国产精品av| 国产伦精品一区二区三区在线播放| 欧美精品一区二区三区精品| 久久精品亚洲| 国产欧美日韩一区二区三区在线| 激情综合网五月| 日韩欧美午夜| 丝袜av一区| 国产一区二区三区久久| 欧美日韩1区2区3区| 国产精品婷婷| 精品一区毛片| 国产超碰精品| 成人在线黄色| 国产精品久久国产愉拍| 日韩精品三级| 亚洲精品少妇| 亚洲午夜久久| 免费人成精品欧美精品| 久久中文视频| 日韩欧美字幕| 鲁鲁在线中文| a天堂资源在线| 韩国久久久久久| 91av亚洲| 99热精品久久| 欧美日韩一二三四| 亚洲二区在线| 亚洲国产专区校园欧美|