某個(gè)執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境被銷毀怖亭,保存在其中的所有變量和函數(shù)定義也誰(shuí)之銷毀(全局執(zhí)行環(huán)境直到應(yīng)用程序退出——例如關(guān)閉網(wǎng)頁(yè)或者瀏覽器時(shí)才會(huì)銷毀侥蒙。
通過(guò)之前的學(xué)習(xí)崔兴,我們知道通過(guò)函數(shù)作用域我們可以將內(nèi)部的變量給隱藏起來(lái)五督,此時(shí)會(huì)使得外部作用域無(wú)法訪問(wèn)包裝函數(shù)內(nèi)部的任何內(nèi)容藏否。但是使用這種技術(shù)來(lái)隱藏作用域也帶來(lái)了一些問(wèn)題:首先,函數(shù)名也污染了所處的作用域概荷;其次秕岛,必須每次顯式調(diào)用函數(shù)碌燕。
而我們所期望看到的是這樣的:函數(shù)不需要函數(shù)名(跟確切的說(shuō)是函數(shù)名不會(huì)污染所在作用域)误证;能夠自動(dòng)運(yùn)行。值得慶幸的是修壕,JavaScript提供了方法以實(shí)現(xiàn)上述兩個(gè)目標(biāo):立執(zhí)行函數(shù)表達(dá)式(IIFE)愈捅。
先不討論立執(zhí)行函數(shù)表達(dá)式的細(xì)節(jié),這里我們先討論一下弄清函數(shù)表達(dá)式和函數(shù)聲明的重要性慈鸠。我們知道函數(shù)聲明可以聲明提升蓝谨,函數(shù)聲明必須使用具名函數(shù)。那么問(wèn)題來(lái)了青团,怎么區(qū)分函數(shù)聲明和函數(shù)表達(dá)式呢譬巫?答案就是:看function關(guān)鍵字出現(xiàn)在整個(gè)聲明中的位置,如果function是出現(xiàn)在整個(gè)聲明中的第一個(gè)詞的話督笆,那么他就是一個(gè)函數(shù)聲明芦昔,否則就是一個(gè)函數(shù)表達(dá)式。
匿名函數(shù)和具名那個(gè)函數(shù)相比較的缺點(diǎn):1.棧追蹤時(shí)沒(méi)有有意義的函數(shù)名娃肿,使得調(diào)試?yán)щy咕缎;2.忽略了可讀性珠十。所以,我們需要記住的時(shí):始終給函數(shù)表達(dá)式命名是一個(gè)最佳實(shí)踐凭豪。所以說(shuō)焙蹭,使用具名函數(shù)的立執(zhí)行函數(shù)也是更加值得推廣。像下面這樣:
var a = 2;
(function IIFE(){var a = 2; console.log(a);})();//3
console.log(a);//2
將IIFE用于UMD嫂伞。
var a = 2;
(function IIFE(def){def(window);})(function def(global){
var a = 3;
console.log(a);
console.log(global.a);
});
1.塊作用域
JavaScript中并不存在什么塊作用域孔厉,所以像下面這樣的語(yǔ)言結(jié)構(gòu)盡管在其他語(yǔ)言里面代表了塊作用域,但是在JavaScript中他什么都不是帖努。比方說(shuō):
for(var i = 0; i < 10; i++){
console.log(i);
}
需要指出來(lái)的是烟馅,這里的變量i并不是在for塊中生存的。更進(jìn)一步說(shuō)明的話然磷,如果這個(gè)for是在全局作用域被調(diào)用的話郑趁,那么變量i就屬于全局作用域。WTF
var foo = true
if(foo){
var bar = foo * 2
bar = fun(bar)
console.log(bar)
}
上面的bar聲明簡(jiǎn)直就是莫名其妙對(duì)吧姿搜,明明沒(méi)有塊作用域寡润,卻一本正經(jīng)的在一個(gè)塊中聲明變量,用這種寫法很容易使我們產(chǎn)生誤解舅柜。所以必須謹(jǐn)記的是:除了某些特殊情況之外梭纹,并不存在什么塊作用域。
當(dāng)然致份,如果你細(xì)究JavaScript的話变抽,那么還是會(huì)發(fā)現(xiàn)存在某些特殊的塊作用域的〉椋總結(jié)來(lái)說(shuō)绍载,也就下面這幾種場(chǎng)合之下:
- try/catch的catch塊
- let
- const
try/catch中的catch塊
try/catch中的catch塊會(huì)創(chuàng)建一個(gè)塊作用域,其中聲明的變量?jī)H在catch塊中有效:
try{
undefined()//TypeError
}catch(err){
console.log(err)
}
console.log(err)//ReferenceError
從上面的例子滔蝉,我們可以看出在catch塊中聲明的err變量?jī)H在catch代碼塊中能被訪問(wèn)的到击儡,在外界訪問(wèn)的話直接就reference error了,所以說(shuō)catch代碼塊可以形成一個(gè)塊作用域蝠引。
let和const
在ES6中新引入的let和const關(guān)鍵字所定義的變量?jī)H存在定義位置所在的那個(gè)代碼塊阳谍,所以說(shuō)這個(gè)時(shí)候也形成了一個(gè)塊作用域。舉個(gè)例子:
var foo = true
if(foo){
let bar = foo * 2
bar = fun(bar)
console.log(bar)
}
console.log(bar)//referenceerror
利用let和const定義變量的時(shí)候螃概,均不會(huì)使得變量進(jìn)行聲明提升矫夯。看個(gè)例子:
{
console.log(bar)//referenceerror
let bar = 2
}
{
console.log(baz)//undefined
var baz = 2
}
關(guān)于作用域問(wèn)題的總結(jié),除了某些特殊的情況之外是不存在塊作用域的吊洼。這些特殊情況分別是:try/catch训貌,let,const融蹂。
END