1. 作用域
作用域就是代碼的執(zhí)行環(huán)境,全局執(zhí)行環(huán)境就是全局作用域关带,函數(shù)的執(zhí)行環(huán)境就是私有作用域侥涵,它們都是棧內(nèi)存。
執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問(wèn)的其他數(shù)據(jù)宋雏,決定了它們各自的行為芜飘。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中磨总。雖然我們編寫(xiě)的代碼無(wú)法訪問(wèn)這個(gè)對(duì)象嗦明,但解析器在處理數(shù)據(jù)時(shí)會(huì)在后臺(tái)使用它。
全局執(zhí)行環(huán)境是最外圍的一個(gè)執(zhí)行環(huán)境蚪燕。根據(jù) ECMAScript 實(shí)現(xiàn)所在的宿主環(huán)境不同娶牌,表示的執(zhí)行環(huán)境的對(duì)象也不一樣。
- 在 Web 瀏覽器中馆纳,全局執(zhí)行環(huán)境被認(rèn)為是
window
對(duì)象诗良,因此所有全局變量和函數(shù)都是作為window
對(duì)象的屬性和方法創(chuàng)建的。 - 在 NODE 環(huán)境中鲁驶,全局執(zhí)行環(huán)境是
global
對(duì)象累榜。
某個(gè)執(zhí)行環(huán)境中所有的代碼執(zhí)行完畢后,該環(huán)境被銷毀,保存在其中的所有變量和函數(shù)定義也隨之銷毀(全局執(zhí)行環(huán)境直到應(yīng)用程序退出時(shí)壹罚,如關(guān)閉瀏覽器或網(wǎng)頁(yè)葛作,才會(huì)被銷毀)
每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí)猖凛,函數(shù)的環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中赂蠢。而在函數(shù)執(zhí)行之后,棧將被環(huán)境彈出辨泳,把控制權(quán)返回給之前的執(zhí)行環(huán)境虱岂。ECMAScript 程序中的執(zhí)行流正是由這個(gè)方便的機(jī)制控制著。
概括來(lái)說(shuō):
作用域就是代碼執(zhí)行開(kāi)辟棧內(nèi)存
- 私有作用域 ----> 函數(shù)執(zhí)行都會(huì)形成一個(gè)私有作用域
- 全局作用域 ----> 頁(yè)面一打開(kāi)就會(huì)形成一個(gè)全局的作用域
- 私有變量 ----> 在私有作用域里邊形成的變量 (通過(guò) var 聲明菠红; 形參)
- 全局變量 ----> 在全局作用域形成的變量(var a = 12 或者函數(shù)內(nèi)沒(méi)有聲明第岖,直接賦值的變量)
2. 作用域鏈
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈(作用域形成的鏈條)
- 作用域鏈的前端试溯,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象
- 作用域鏈中的下一個(gè)對(duì)象來(lái)自于外部環(huán)境蔑滓,而在下一個(gè)變量對(duì)象則來(lái)自下一個(gè)外部環(huán)境,一直到全局執(zhí)行環(huán)境
- 全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈上的最后一個(gè)對(duì)象
內(nèi)部環(huán)境可以通過(guò)作用域鏈訪問(wèn)所有外部環(huán)境遇绞,但外部環(huán)境不能訪問(wèn)內(nèi)部環(huán)境的任何變量和函數(shù)键袱。
var n = 10;
function outer(){
function inner(){
function center(){
console.log(n);
}
center();
}
inner();
var n = 15;
}
outer(); //=> undefined
如函數(shù)的執(zhí)行,形成一個(gè)私有作用域摹闽,形參和當(dāng)前私有作用域中聲明的變量都是私有變量蹄咖,保存在內(nèi)部的一個(gè)變量對(duì)象中,其下一個(gè)外部環(huán)境可能是函數(shù)付鹿,也就包含了函數(shù)的內(nèi)部變量對(duì)象澜汤,直到全局作用域。
當(dāng)在內(nèi)部函數(shù)中舵匾,需要訪問(wèn)一個(gè)變量的時(shí)候银亲,首先會(huì)訪問(wèn)函數(shù)本身的變量對(duì)象,是否有這個(gè)變量纽匙,如果沒(méi)有,那么會(huì)繼續(xù)沿作用域鏈往上查找拍谐,直到全局作用域烛缔。如果在某個(gè)變量對(duì)象中找到則使用該變量對(duì)象中的變量值。
由于變量的查找是沿著作用域鏈來(lái)實(shí)現(xiàn)的轩拨,所以也稱作用域鏈為變量查找的機(jī)制践瓷。
這個(gè)機(jī)制也說(shuō)明了訪問(wèn)局部變量要比訪問(wèn)全局變量更快,因?yàn)橹虚g的查找過(guò)程更短亡蓉。但是 JavaScript 引擎在優(yōu)化標(biāo)識(shí)符查詢方面做得很好晕翠,因此這個(gè)差別可以忽略不計(jì)。