第四章的篇幅突然小了些,但是內(nèi)容卻是干貨十足,有些問題比如延長作用域,想再弄得透徹一些,還是需要多看一些書外的資料的。
此外沒有塊級作用域的這個點(diǎn),很容易成為面試中考察的地方。典型的就是 for 和 if 語句中的初始化變量是存在于循環(huán)體外部的環(huán)境中的。不過在 ECMAScript 6 中,增加的 let 關(guān)鍵字卻可以實(shí)現(xiàn)將其定義的變量固化到花括號的作用域中,同時,這個變量是不存在變量提升特性的,也就是說先定義才能使用。
執(zhí)行環(huán)境及作用域
執(zhí)行環(huán)境(execution context)定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為。
每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象,但解析器在處理數(shù)據(jù)時會在后臺使用它。
全局執(zhí)行環(huán)境是最外圍的一個執(zhí)行環(huán)境。根據(jù) ECMAScript 實(shí)現(xiàn)所在的宿主環(huán)境不同,表示執(zhí)行環(huán)境的對象也不一樣。在 Web 瀏覽器中,全局執(zhí)行環(huán)境被認(rèn)為是 window 對象,因此所有的全局變量和函數(shù)都是作為 window 對象的屬性和方法創(chuàng)建的。某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀(全局執(zhí)行環(huán)境直到應(yīng)用程序退出——如關(guān)閉網(wǎng)頁或?yàn)g覽器——時才會被銷毀)。
每個函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。ECMAScript 程序中的執(zhí)行流正是由這個方便的機(jī)制控制著。
當(dāng)代碼在一個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的一個作用域鏈(scope chain)。作用域鏈的用途,是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象。如果這個環(huán)境是函數(shù),則將其活動對象(activation object)作為變量對象?;顒訉ο笤谧铋_始時只包含一個變量,即 arguments 對象(這個對象在全局環(huán)境中式不存在的)。作用域鏈中的下一個變量對象來自包含(外部)環(huán)境,而再下一個變量對象則來自下一個包含環(huán)境。這樣,一直延續(xù)到全局執(zhí)行環(huán)境;全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈中的最后一個對象。
標(biāo)識符解析是沿著作用域鏈一級一級地搜索標(biāo)識符的過程。搜索過程始終從作用域的前端開始,然后逐級地向后回溯,直至找到標(biāo)識符未知(如果找不到標(biāo)識符,通常會導(dǎo)致錯誤發(fā)生)。
