先來看這么一段代碼
var a = 1
function foo() {
if(false){
var a = 1
}
console.log('a:'+a);//a = undefined
}
foo()
為什么會這樣? if條件語句明明就沒有執(zhí)行眯杏,我們打印的應該是全局變量a才對匾乓。
其實很簡單,首先要知道js查詢變量的機制鳄厌。
下面給出《javascript高級程序設(shè)計3》中的解釋荞胡。
當在某個環(huán)境中為了讀取或?qū)懭攵靡粋€標識符時,必須通過搜索來確定該標識符實際代表什么了嚎。搜索過程從作用域鏈的前端開始泪漂,向上逐級查詢與給定名字匹配的標識符廊营。如果在局部環(huán)境中找到了該標識符,搜索過程停止萝勤,變量就緒露筒。如果在局部環(huán)境中沒有找到該變量名,則繼續(xù)沿作用域鏈向上搜索敌卓。搜索過程將一直追溯到全局環(huán)境的變量對象邀窃。如果在全局環(huán)境中也沒有找到這個標識符,則意味著該變量尚未聲明假哎。
通過下面這個示例瞬捕,可以理解查詢標識符的過程
var color = "blue";
function getColor(){
return color;
}
alert(getColor()); //"blue"
調(diào)用本例中的函數(shù)getColor()時會引用變量color。為了確定變量color 的值舵抹,將開始一個兩步的搜索過程肪虎。首先,搜索getColor()的變量對象惧蛹,查找其中是否包含一個名為color 的標識符扇救。在沒有找到的情況下,搜索繼續(xù)到下一個變量對象(全局環(huán)境的變量對象)香嗓,然后在那里找到了名為color 的標識符迅腔。因為搜索到了定義這個變量的變量對象,搜索過程宣告結(jié)束靠娱。
在這個搜索過程中沧烈,如果存在一個局部的變量的定義,則搜索會自動停止像云,不再進入另一個變量對象锌雀。換句話說,如果局部環(huán)境中存在著同名標識符迅诬,就不會使用位于父環(huán)境中的標識符腋逆,如下面的例子所示:
var color = "blue";
function getColor(){
var color = "red";
return color;
}
alert(getColor()); //"red"
修改后的代碼在getColor()函數(shù)中聲明了一個名為color 的局部變量。調(diào)用函數(shù)時侈贷,該變量就會被聲明惩歉。而當函數(shù)中的第二行代碼執(zhí)行時,意味著必須找到并返回變量color 的值俏蛮。搜索過程首先從局部環(huán)境中開始撑蚌,而且在這里發(fā)現(xiàn)了一個名為color 的變量,其值為"red"嫁蛇。因為變量已經(jīng)找到了锨并,所以搜索即行停止,return 語句就使用這個局部變量睬棚,并為函數(shù)會返回"red"。也就是說,任何位于局部變量color 的聲明之后的代碼抑党,如果不使用window.color 都無法訪問全局color變量包警。
好,到這里問題又來了底靠,開始例子中并沒有聲明局部變量a昂蕖(if條件語句永遠不執(zhí)行),那為什么仍然訪問不到?
這里要引入變量提升的概念暑中。先看一段代碼
console.log('a:'+a)//a:undefined
var a = 1
console.log('a:'+a)//a:1
實際上壹瘟,JavaScript 中,變量可以在使用后聲明鳄逾,也就是變量可以先使用再聲明稻轨。
變量提升:函數(shù)聲明和變量聲明總是會被解釋器悄悄地被"提升"到方法體的最頂部。
但JavaScript 只有聲明的變量會提升雕凹,初始化的不會殴俱。
所以這也是為什么沒有報錯,但a卻是undefined而不是1的原因枚抵。
這段代碼可以等效于這樣
var a
console.log('a:'+a)// a:undefined
a = 1
console.log('a:'+a)//a:1
好啦线欲,知道了這個后,上面那個問題就很簡單了汽摹。
實際上李丰,雖然if條件語句里的代碼并沒有執(zhí)行,但解釋器一開始就會進行變量提升逼泣。等效于這樣:
var a = 1
function foo() {
var a
if(false){
a = 1
}
console.log('a:'+a);//a = undefined
}
foo()
由于變量提升嫌套,在函數(shù)里就已經(jīng)搜索到了局部變量a,繼而停止搜索圾旨。所以最終無法獲取全局變量a踱讨,出現(xiàn)問題。
PS:從這里可以看出砍的,變量提升這個功能是為了防止某些錯誤的發(fā)生而遷就了開發(fā)者痹筛,但實際上,這個功能可能反而是不利的