執(zhí)行環(huán)境(Execution context,EC)或執(zhí)行上下文,是JS中一個(gè)極為重要的概念
EC的組成
當(dāng)JavaScript代碼執(zhí)行的時(shí)候,會(huì)進(jìn)入不同的執(zhí)行上下文,這些執(zhí)行上下文會(huì)構(gòu)成了一個(gè)執(zhí)行上下文棧(Execution context stack,ECS)。

EC的組成
- 變量對(duì)象(Variable object,VO): 變量對(duì)象即包含變量的對(duì)象,除了我們無(wú)法訪(fǎng)問(wèn)它外,和普通對(duì)象沒(méi)什么區(qū)別。
-
[[Scope]]屬性:作用域即變量對(duì)象,作用域鏈?zhǔn)且粋€(gè)由變量對(duì)象組成的帶頭結(jié)點(diǎn)的單向鏈表,其主要作用就是用來(lái)進(jìn)行變量查找。而[[Scope]]屬性是一個(gè)指向這個(gè)鏈表頭節(jié)點(diǎn)的指針。 - this: 指向一個(gè)環(huán)境對(duì)象,注意是一個(gè)對(duì)象,而且是一個(gè)普通對(duì)象,而不是一個(gè)執(zhí)行環(huán)境。
產(chǎn)生EC的兩個(gè)階段
當(dāng)一段JS代碼執(zhí)行的時(shí)候,JS解釋器會(huì)通過(guò)兩個(gè)階段去產(chǎn)生一個(gè)EC
- 創(chuàng)建階段(當(dāng)函數(shù)被調(diào)用,但是開(kāi)始執(zhí)行函數(shù)內(nèi)部代碼之前)
- 創(chuàng)建變量對(duì)象VO
- 設(shè)置
[[Scope]]屬性的值 - 設(shè)置this的值
- 激活/代碼執(zhí)行階段
初始化變量對(duì)象,即設(shè)置變量的值、函數(shù)的引用,然后解釋/執(zhí)行代碼。
創(chuàng)建變量對(duì)象VO
- 根據(jù)函數(shù)的參數(shù),創(chuàng)建并初始化arguments object
- 掃描函數(shù)內(nèi)部代碼,查找函數(shù)聲明(function declaration)
- 對(duì)于所有找到的函數(shù)聲明,將函數(shù)名和函數(shù)引用存入VO中
- 如果VO中已經(jīng)有同名函數(shù),那么就進(jìn)行覆蓋
//函數(shù)聲明
function f(){}
- 掃描函數(shù)內(nèi)部代碼,查找變量聲明(Variable declaration)
- 對(duì)于所有找到的變量聲明,將變量名存入VO中,并初始化為undefined
- 如果變量名跟已經(jīng)聲明的形參或函數(shù)相同,則什么也不做
//變量聲明,必須通過(guò)var關(guān)鍵字聲明
var example = 'example'
注:步驟2和3也稱(chēng)為聲明提升(declaration hoisting)
執(zhí)行環(huán)境的分類(lèi)
- 全局執(zhí)行環(huán)境
在瀏覽器中,其指window對(duì)象,是JS代碼開(kāi)始運(yùn)行時(shí)的默認(rèn)環(huán)境。
全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈中的最后一個(gè)對(duì)象。 - 函數(shù)執(zhí)行環(huán)境
當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)先創(chuàng)建一個(gè)執(zhí)行環(huán)境及相應(yīng)的作用域鏈。然后使用arguments和其他命名參數(shù)的值來(lái)初始化執(zhí)行環(huán)境的變量對(duì)象。
注:上面的分類(lèi)也說(shuō)明了JS中只有兩種作用域(作用域即變量對(duì)象):全局作用域、函數(shù)作用域,并沒(méi)有塊級(jí)作用域,更沒(méi)有對(duì)象作用域(見(jiàn)最后的例子,但是with語(yǔ)句是一個(gè)例外,其可以臨時(shí)在作用域鏈的前端增加一個(gè)普通對(duì)象)
this
見(jiàn)文章:JS中的this
參考資料
JavaScript的執(zhí)行上下文, 五星級(jí)好文
JavaScript中的this
理解JavaScript中的作用域鏈
Scope chain
JavaScript高級(jí)程序設(shè)計(jì)