多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > web前端 > htmlcss > JavaScript學習--Item19 執行上下文(execution context)

JavaScript學習--Item19 執行上下文(execution context)

來源:程序員人生   發布時間:2016-06-24 08:40:03 閱讀次數:3821次


在這篇文章里,我將深入研究JavaScript中最基本的部份——履行上下文(execution context)。讀完本文后,你應當清楚了解釋器做了甚么,為何函數和變量能在聲明前使用和他們的值是如何決定的。

1、EC—履行環境或履行上下文

每當控制器到達ECMAScript可履行代碼的時候,控制器就進入了1個履行上下文(好高大上的概念啊)。

javascript中,EC分為3種:

  • 全局級別的代碼 –– 這個是默許的代碼運行環境,1旦代碼被載入,引擎最早進入的就是這個環境。
  • 函數級別的代碼 ––當履行1個函數時,運行函數體中的代碼。
  • Eval的代碼 –– 在Eval函數內運行的代碼。

EC建立分為兩個階段:進入履行上下文(創建階段)履行階段(激活/履行代碼)

  1. 進入上下文階段:產生在函數調用時,但是在履行具體代碼之前(比如,對函數參數進行具體化之前)
    • 創建作用域鏈(Scope Chain)
    • 創建變量,函數和參數。
    • 求”this“的值。
  2. 履行代碼階段
    • 變量賦值
    • 函數援用
    • 解釋/履行其他代碼。

我們可以將EC看作是1個對象。

EC={ VO:{/* 函數中的arguments對象, 參數, 內部的變量和函數聲明 */}, this:{}, Scope:{ /* VO和所有父履行上下文中的VO */} }

現在讓我們看1個包括全局和函數上下文的代碼例子:

這里寫圖片描述

很簡單的例子,我們有1個被紫色邊框圈起來的全局上下文和3個分別被綠色,藍色和橘色框起來的不同函數上下文。只有全局上下文(的變量)能被其他任何上下文訪問。

你可以有任意多個函數上下文,每次調用函數創建1個新的上下文,會創建1個私有作用域,函數內部聲明的任何變量都不能在當前函數作用域外部直接訪問。在上面的例子中,函數能訪問當前上下文外面的變量聲明,但在外部上下文不能訪問內部的變量/函數聲明。為何會產生這類情況?代碼究竟是如何被解釋的?

2、ECS—履行上下文棧

1系列活動的履行上下文從邏輯上構成1個棧。棧底總是全局上下文,棧頂是當前(活動的)履行上下文。當在不同的履行上下文間切換(退出的而進入新的履行上下文)的時候,棧會被修改(通過壓棧或退棧的情勢)。

壓棧:全局EC—>局部EC1—>局部EC2—>當前EC
出棧:全局EC<—局部EC1<—局部EC2<—當前EC

我們可以用數組的情勢來表示環境棧:

ECS=[局部EC,全局EC];

每次控制器進入1個函數(哪怕該函數被遞歸調用或作為構造器),都會產生壓棧的操作。進程類似javascript數組的push和pop操作。

閱讀器里的JavaScript解釋器被實現為單線程。這意味著同1時間只能產生1件事情,其他的行文或事件將會被放在叫做履行棧里面排隊。下面的圖是單線程棧的抽象視圖:
這里寫圖片描述

我們已知道,當閱讀器首次載入你的腳本,它將默許進入全局履行上下文。如果,你在你的全局代碼中調用1個函數,你程序的時序將進入被調用的函數,并穿件1個新的履行上下文,并將新創建的上下文壓入履行棧的頂部。

如果你調用當前函數內部的其他函數,相同的事情會在此上演。代碼的履行流程進入內部函數,創建1個新的履行上下文并把它壓入履行棧的頂部。閱讀器將總會履行棧頂的履行上下文,1旦當前上下文函數履行結束,它將被從棧頂彈出,并將上下文控制權交給當前的棧。下面的例子顯示遞歸函數的履行棧調用進程:

(function foo(i) { if (i === 3) { return; } else { foo(++i); } }(0));

這里寫圖片描述

這代碼調用自己3次,每次給i的值加1。每次foo函數被調用,將創建1個新的履行上下文。1旦上下文履行終了,它將被從棧頂彈出,并將控制權返回給下面的上下文,直到只剩全局上下文能為止。

有5個需要記住的關鍵點,關于履行棧(調用棧):

  • 單線程。
  • 同步履行。
  • 1個全局上下文。
  • 無窮制函數上下文。
  • 每次函數被調用創建新的履行上下文,包括調用自己。

3、VO—變量對象

每個EC都對應1個變量對象VO,在該EC中定義的所有變量和函數都寄存在其對應的VO中。

VO分為全局上下文VO(全局對象,Global object,我們通常說的global對象)和函數上下文的AO。

VO: { // 上下文中的數據 ( 函數形參(function arguments), 函數聲明(FD),變量聲明(var)) }

1. 進入履行上下文時,VO的初始化進程具體以下:

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

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

變量聲明(var,VariableDeclaration) —— 變量對象的1個屬性,其屬性名即為變量名,其值為undefined;如果變量名和已聲明的函數名或函數的參數名相同,則不會影響已存在的屬性。
注意:該進程是有前后順序的。

2. 履行代碼階段時,VO中的1些屬性undefined值將會肯定。

4、AO活動對象

在函數的履行上下文中,VO是不能直接訪問的。它主要扮演被稱作活躍對象(activation object)(簡稱:AO)的角色。
這句話怎樣理解呢,就是當EC環境為函數時,我們訪問的是AO,而不是VO。

VO(functionContext) === AO;

AO是在進入函數的履行上下文時創建的,并為該對象初始化1個arguments屬性,該屬性的值為Arguments對象。

AO = { arguments: { callee:, length:, properties-indexes: //函數傳參參數值 } };

FD的情勢只能是以下這樣:

function f(){ }

當函數被調用是executionContextObj被創建,但在實際函數履行之前。這是我們上面提到的第1階段,創建階段。在此階段,解釋器掃描傳遞給函數的參數或arguments,本地函數聲明和本地變量聲明,并創建executionContextObj對象。掃描的結果將完成變量對象的創建。

內部的履行順序以下:

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

2、履行函數代碼之前,先創建履行上下文。
3、進入創建階段:

  • 初始化作用域鏈:
  • 創建變量對象:
  • 創建arguments對象,檢查上下文,初始化參數名稱和值并創建援用的復制。
  • 掃描上下文的函數聲明:為發現的每個函數,在變量對象上創建1個屬性(確切的說是函數的名字),其有1個指向函數在內存中的援用。如果函數的名字已存在,援用指針將被重寫。
  • 掃面上下文的變量聲明:為發現的每一個變量聲明,在變量對象上創建1個屬性——就是變量的名字,并且將變量的值初始化為undefined,如果變量的名字已在變量對象里存在,將不會進行任何操作并繼續掃描。
  • 求出上下文內部“this”的值。

4、激活/代碼履行階段:
在當前上下文上運行/解釋函數代碼,并隨著代碼1行行履行指派變量的值。

示例

1、具體實例

function foo(i) { var a = ‘hello‘; var b = function privateB() { }; function c() { } } foo(22);

當調用foo(22)時,創建狀態像下面這樣:

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

真如你看到的,創建狀態負責處理定義屬性的名字,不為他們指派具體的值,和形參/實參的處理。1旦創建階段完成,履行流進入函數并且激活/代碼履行階段,看下函數履行完成后的模樣:

fooExecutionContext = { scopeChain: { ... }, variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: ‘hello‘, b: pointer to function privateB() }, this: { ... } }

2、VO示例:

alert(x); // function var x = 10; alert(x); // 10 x = 20; function x() {}; alert(x); // 20

進入履行上下文時,

ECObject={ VO:{ x:<reference to FunctionDeclaration "x"> } };

履行代碼時:

ECObject={ VO:{ x:20 //與函數x同名,替換掉,先是10,后變成20 } };

對以上的進程,我們詳細解釋下。

在進入上下文的時候,VO會被填充函數聲明; 同1階段,還有變量聲明“x”,但是,正如此條件到的,變量聲明是在函數聲明和函數形參以后,并且,變量聲明不會對已存在的一樣名字的函數聲明和函數形參產生沖突。因此,在進入上下文的階段,VO填充為以下情勢:

VO = {}; VO['x'] = <援用了函數聲明'x'> // 發現var x = 10; // 如果函數“x”還未定義 // 則 "x" 為undefined, 但是,在我們的例子中 // 變量聲明其實不會影響同名的函數值 VO['x'] = <值不受影響,還是函數>

履行代碼階段,VO被修改以下:

VO['x'] = 10; VO['x'] = 20;

以下例子再次看到在進入上下文階段,變量存儲在VO中(因此,雖然else的代碼塊永久都不會履行到,而“b”卻依然在VO中)

if (true) { var a = 1; } else { var b = 2; } alert(a); // 1 alert(b); // undefined, but not "b is not defined"

3、AO示例:

function test(a, b) { var c = 10; function d() {} var e = function _e() {}; (function x() {}); } test(10); // call

當進入test(10)的履行上下文時,它的AO為:

testEC={ AO:{ arguments:{ callee:test length:1, 0:10 }, a:10, c:undefined, d:<reference to FunctionDeclaration "d">, e:undefined } };

因而可知,在建立階段,VO除arguments,函數的聲明,和參數被賦予了具體的屬性值,其它的變量屬性默許的都是undefined。函數表達式不會對VO造成影響,因此,(function x() {})其實不會存在于VO中。

履行 test(10)時,它的AO為:

testEC={ AO:{ arguments:{ callee:test, length:1, 0:10 }, a:10, c:10, d:<reference to FunctionDeclaration "d">, e:<reference to FunctionDeclaration "e"> } };

可見,只有在這個階段,變量屬性才會被賦具體的值。

5、提升(Hoisting)解密

在之前的JavaScript Item中降到了變量和函數聲明被提升到函數作用域的頂部。但是,沒有人解釋為何會產生這類情況的細節,學習了上面關于解釋器如何創建active活動對象的新知識,很容易明白為何。看下面的例子:

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

我們能回答下面的問題:

1、為何我們能在foo聲明之前訪問它?
如果我們跟隨創建階段,我們知道變量在激活/代碼履行階段已被創建。所以在函數開始履行之前,foo已在活動對象里面被定義了。

2、foo被聲明了兩次,為何foo顯示為函數而不是undefined或字符串?
雖然foo被聲明了兩次,我們知道從創建階段函數已在活動對象里面被創建,這1進程產生在變量創建之前,并且如果屬性名已在活動對象上存在,我們僅僅更新援用。
因此,對foo()函數的援用首先被創建在活動對象里,并且當我們解釋到var foo時,我們看見foo屬性名已存在,所以代碼甚么都不做并繼續履行。

3、為何bar的值是undefined?
bar實際上是1個變量,但變量的值是函數,并且我們知道變量在創建階段被創建但他們被初始化為undefined。


生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 色妞影视 | 亚洲成人毛片 | 波多野结衣一二区 | 精品久久久久久久 | 欧美yw精品日本国产精品 | 91福利国产在线观一区二区 | 另类图片综合网 | 亚洲一逼 | 情侣偷偷看的羞羞视频网站 | 欧美午夜春性猛交xxxx | 精品1州区2区3区4区产品乱码 | 亚洲最大福利视频 | 2019在线亚洲成年视频网站 | 一二三四在线观看视频 | jizz视频| 五月婷婷伊人 | 极品美女国产精品免费一区 | 一级做a爰片久久毛片欧美 一级做a爰片久久毛片人呢 | 亚洲图片偷拍自拍 | 中文字幕免费在线观看 | www.黄色网址.com | 亚洲女人的天堂 | 性xxxxx外性hd | 黄大片日本一级在线a | 久久精品免看国产 | 国产成人免费视频精品一区二区 | 在线播放人成午夜免费视频 | 亚洲欧美日韩专区一 | 久久大香线蕉综合爱 | 国产综合久久久久 | 自怕偷自怕亚洲精品 | 美女无遮挡免费视频观看网站 | 亚洲国产日韩欧美在线vip1区 | 中文字幕二区 | 欧美日韩亚洲高清不卡一区二区三区 | 日本护士做xxxxxhd | 色老久久精品偷偷鲁一区 | 一二三四视频免费观看在线看 | 波多野结衣在线观看一区二区 | 久久精品二三区 | 日韩一级精品视频在线观看 |