這段可執行代碼就是編譯階段生成的
),會建立與之對應的執行上下文(Excution Context簡稱EC
)。執行上下文能夠理解爲執行環境(執行上下文只能由JS解釋器建立,也只能由JS解釋器使用,用戶是不能夠操做該"對象"的
)。<script>xxx</script>
標籤,就是進入一個全局執行環境先進後出,後進先出
」,這種棧稱之爲執行上下文棧(Excution Context Stack 簡稱ECS
)。棧底
永遠是全局執行上下文,有且僅有一個
瀏覽器關閉
時,纔會彈出棧數量沒有限制
棧頂
永遠是當前活動執行上下文
,其他的都處於等待狀態中,一旦執行完畢,當即彈出棧,而後控制權交回下一個執行上下文每次被調用
時,纔會爲其建立執行上下文,函數被聲明時是沒有的。全局 VO -> GO
函數 VO -> AO
此階段主要完成三件事件,一、建立變量對象 二、創建做用域鏈 三、肯定this指向javascript
此階段主要完成變量賦值、函數調用、其餘操做java
一、根據函數參數,建立並初始化arguments
對象,編程
AO={ //建立arguments對象,注意這裏尚未添加任何屬性 arguments:{ length:0 //設置初始值爲0 } }
三、查找var變量聲明(查找變量時,會把函數的參數等價於var聲明,因此在AO中也會添加和參數名同樣的屬性,初始值也是undefined
),在變量對象添加屬性,屬性名就是變量名,屬性值是undefined,若是已經存在同名的,則不處理
查找var變量聲明時,是從函數的參數開始的,此時函數的參數定義至關於以下代碼數組
Foo(a, b, c){ //-------------------這一部分是隱藏的咱們沒法顯示看到------------------------- var a=1; //傳入參數 var b=2; //傳入參數 var c; //沒有傳入參數 //-------------------------------------------- var name="kity" function bar(){} } Foo(11,22)
同名標識符(函數、變量)
,則函數能夠覆蓋變量
,函數的優先級高於變量變量對象(OV)
和激活對象(AO)
是同一個東西,在全局執行上下文中是VO
,在函數執行上下文中是AO
,由於VO在全局執行上下文中能夠直接訪問
,可是在函數執行上下文中是不能直接訪問
的,此時有AO扮演VO
的角色以以下代碼爲例瀏覽器
var g_name="tom"; var g_age=20; function g_fn(num){ var l_name="kity"; var l_age=18; function l_fn(){ console.log(g_name + '===' + l_name + '===' + num); } } g_fn(10);
當JS控制器轉到這一段代碼時,會建立一個執行上下文,G_EC
執行上下文的結構大概以下:數據結構
G_EC = { VO : {}, Scope_chain : [], this : {} } /* VO的結構大概 */ VO = { g_name : undefined, g_age : undefined, g_fn : <函數在內存中引用值> } /* Scope_chain的大概結構以下 */ Scope_chain = [ G_EC.VO ] // 數組中第一個元素是當前執行上下文的VO,第二個是父執行上下文的VO,最後一個是全局執行上下文的VO,在執行階段,會沿着這個做用域鏈一個一個的查找標識符,若是查到則返回,否知一直查找到全局執行上下文的VO /* this */ this = undefined // 此時this的值是undefined
執行上下文一旦建立完畢,就立馬被壓入函數調用棧中,此時解釋器會悄悄的作一件事情,就是給當前VO中的函數添加一個內部屬性[[scope]],該屬性指向上面的做用域鏈。編程語言
g_fn.scope = [ global_EC.VO ] // 該scope屬性只能被JS解釋器所使用,用戶沒法使用
VO->AO
)一行一行執行代碼,當遇到一個表達式時,就會去當前做用域鏈的中查找VO對象
,若是找到則返回,若是找不到,則繼續查找下一個VO對象,直至全局VO對象終止。
此階段能夠有變量賦值,函數調用等操做,當解釋器遇到g_fn()時,就知道這是一個函數調用,而後當即爲其建立一個函數執行上下文,fn_EC,該上下文fn_EC一樣有兩個階段
分別是建立階段和執行階段。
在建立階段,對於函數的執行上下文,首先會從函數的參數能夠逐行執行,函數
AO={測試
arguments:{ length:0 }
}this
特別說明:執行階段,當函數參數執行賦值操做時,若是AO中有同名的函數存在,則直接跳過,同時會將arguments對象中相應的屬性值修改爲該函數值,這是一個坑啊
function test(a, b) { console.log(a);//1 c = 0; var c; a = 3; b = 2; console.log(b);//2 function b() { } function d() { } console.log(b);//2 } test(1)
各個瀏覽器測試狀況以下: