執(zhí)行環(huán)境/執(zhí)行上下文(Execution Context)
- 執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的數(shù)據(jù)。
- 每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中势似。
- 對(duì)于瀏覽器來說全局對(duì)象是window。所有全局變量和函數(shù)都是作為 window 對(duì)象的屬性和方法創(chuàng)建的餐曹。
- 某個(gè)執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后于颖,該環(huán)境被銷毀柏锄,保存在其中的所有變量和函數(shù)定義也隨之銷毀
執(zhí)行上下文棧(Execution Context Stack
每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境坪仇。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí)杂腰,函數(shù)的環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中。棧底總是全局上下文椅文,棧頂是當(dāng)前(活動(dòng)的)執(zhí)行環(huán)境喂很。 而在函數(shù)執(zhí)行之后蜡镶,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境恤筛。
作用域鏈(Scope Chain)
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈(scope chain)芹橡。作用域鏈的用途毒坛,是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。作用域鏈的前端林说,始終都是當(dāng)前執(zhí)行的代碼所 在環(huán)境的變量對(duì)象煎殷。如果這個(gè)環(huán)境是函數(shù),則將其活動(dòng)對(duì)象(activation object)作為變量對(duì)象腿箩『乐保活動(dòng)對(duì) 象在最開始時(shí)只包含一個(gè)變量,即 arguments 對(duì)象(這個(gè)對(duì)象在全局環(huán)境中是不存在的)珠移。作用域鏈中 的下一個(gè)變量對(duì)象來自包含(外部)環(huán)境弓乙,而再下一個(gè)變量對(duì)象則來自下一個(gè)包含環(huán)境。這樣钧惧,一直延 續(xù)到全局執(zhí)行環(huán)境;全局執(zhí)行環(huán)境的變量對(duì)象始終都是作用域鏈中的最后一個(gè)對(duì)象暇韧。
標(biāo)識(shí)符解析是沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過程。搜索過程始終從作用域鏈的前端開始浓瞪, 然后逐級(jí)地向后回溯懈玻,直至找到標(biāo)識(shí)符為止(如果找不到標(biāo)識(shí)符,通常會(huì)導(dǎo)致錯(cuò)誤發(fā)生)乾颁。
下面舉2個(gè)例子來說明:
var color = "blue";
function changeColor(){
if (color === "blue"){
color = "red";
} else {
color = "blue";
}
}
changeColor();
console.log(color) //red
可以看到函數(shù)的輸出為red涂乌。
在這個(gè)例子中,color是通過函數(shù)作用域找到的(如果是通過參數(shù)傳遞給函數(shù)是不會(huì)影響全局作用域的color英岭,因?yàn)楹瘮?shù)的參數(shù)是按值傳遞的)湾盒。
函數(shù) changeColor()的作用域鏈包含兩個(gè)對(duì)象:它自己的變量對(duì)象(其中定義著 arguments 對(duì)象)和全局環(huán)境的變量對(duì)象∽缑茫可以在函數(shù)內(nèi)部訪問變量 color历涝,就是因?yàn)榭梢栽谶@個(gè)作用域鏈中找到它。
var a = 1
function fn1(){
function fn3(){
function fn2(){
console.log(a)
}
fn2()
var a = 4
}
var a = 2
return fn3
}
var fn = fn1()
fn() //輸出多少
再來看看這個(gè)代碼是怎么執(zhí)行的:
調(diào)用fn漾唉,因?yàn)閒n=fn1()荧库,fn1()返回值是fn3,相當(dāng)于調(diào)用fn3赵刑,fn3又調(diào)用了fn2分衫,最后輸出console.log(a).那么,這個(gè)變量a是什么呢般此?
從上述的概念我們可以知道
作用域鏈應(yīng)該是從fn2到fn3到fn1到全局環(huán)境的蚪战,變量a的查詢是順著作用域鏈的牵现。
fn2中不存在a,就到fn3中查找邀桑,fn3代碼實(shí)際執(zhí)行順序?yàn)?/p>
var a;
fn2();
a=2;
由于變量聲明提升瞎疼,所以fn3變量對(duì)象中存在a。由于函數(shù)執(zhí)行是在賦值前面壁畸,所以最后輸出結(jié)果是undefined