本文大量參考阮一峰老師ES6手冊(cè)魔市。
先是let的使用注意事項(xiàng)
let只在所在的代碼塊以及更深的代碼塊內(nèi)有效
通常就是花括號(hào)包起來(lái)的代碼塊房资,形成的作用域叫塊級(jí)作用域淘这。
if (true) {
let a = 1;
if (true) {
a = 2;
console.log(a); // 2
}
console.log(a); // 2
}
比較特別的for循環(huán)
for循環(huán)還有一個(gè)特別之處穴张,就是設(shè)置循環(huán)變量的那部分是一個(gè)父作用域掉伏,而循環(huán)體內(nèi)部是一個(gè)單獨(dú)的子作用域缝呕。
所以澳窑,for (var i = 0; i < 10; i++)
跟for (let i = 0; i < 10; i++)
就有區(qū)別,下面栗子可以證明:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
我們知道供常,函數(shù)只有被調(diào)用的時(shí)候才會(huì)去考慮作用域摊聋,而且js的作用域特點(diǎn)是詞法作用域,所以:
第一段代碼栈暇,打印10
是因?yàn)?code>i是全局變量麻裁,當(dāng)調(diào)用a[6]
的時(shí)候i
就是10
,所以打印10
沒(méi)毛病源祈。
第二段代碼煎源,a[6]
的console.log(i)
會(huì)去找i
,i
既然是由let
聲明香缺,就被禁錮在for作用域內(nèi)手销,而且for循環(huán)10次能產(chǎn)生10個(gè)作用域,所以i
在那個(gè)作用域的值是6
图张,于是打印6
锋拖。
不變量提升
這個(gè)特性的優(yōu)點(diǎn)我認(rèn)為是:符合人腦思維:先聲明,后使用埂淮。沒(méi)聲明就使用的話姑隅,會(huì)報(bào)錯(cuò)写隶,更醒目倔撞,程序員更容易糾錯(cuò)。就像下面這樣:
// var 的情況
console.log(foo); // 輸出undefined慕趴,不報(bào)錯(cuò)
var foo = 2;
// let 的情況
console.log(bar); // 報(bào)錯(cuò)ReferenceError
let bar = 2;
要我說(shuō)痪蝇,其實(shí)這個(gè)特性沒(méi)什么大用。盡早聲明變量應(yīng)該是寫代碼的起碼規(guī)范冕房。
“暫時(shí)性死區(qū)”(temporal dead zone躏啰,簡(jiǎn)稱 TDZ)
這個(gè)現(xiàn)象是老外提出來(lái)的,但我并沒(méi)有看出它跟“不變量提升”有什么區(qū)別耙册。死區(qū)的原因不就是“變量不提升”+“塊級(jí)作用域”么给僵?
不變量提升也好,暫時(shí)性死區(qū)也好详拙,都是強(qiáng)迫程序員寫出更規(guī)范的代碼帝际,就認(rèn)為是嚴(yán)格模式就得了。
不允許重復(fù)聲明
這個(gè)跟var確實(shí)有鮮明對(duì)比饶辙。
簡(jiǎn)單一句話:銅鑼灣只能有一個(gè)浩南6拙鳌(如果出了銅鑼灣那就隨便了)
if (true) {
let a = 1;
if (true) {
let a = 2;
console.log(a); // 2
}
console.log(a); // 1
}
上面代碼如果把兩個(gè)let
改成var
,那么會(huì)輸出兩個(gè)2
弃揽,就是因?yàn)関ar允許重復(fù)聲明脯爪。
塊級(jí)作用域
塊級(jí)作用域的優(yōu)點(diǎn):
- 模塊化編程则北。
- 不需要特意寫一個(gè)自執(zhí)行函數(shù)來(lái)制造一個(gè)局部作用域。
到底能不能在塊級(jí)作用域聲明函數(shù)痕慢?
ES5是不允許的尚揣,但是ES6又允許了。本著兼容原則守屉,最好是別這么干惑艇,也就是不要在塊級(jí)作用域聲明函數(shù)。如果必須要在塊代碼里寫函數(shù)拇泛,就用函數(shù)表達(dá)式滨巴。
到底啥時(shí)候用var,啥時(shí)候用let俺叭?
var和let各自的適用場(chǎng)合:
- 為了兼容低版本IE恭取,當(dāng)然用var。除此之外熄守,優(yōu)先用let蜈垮。
- 有人說(shuō),es6中用let裕照,babel中用var攒发,原因是babel會(huì)寫很多墊片代碼實(shí)現(xiàn)對(duì)let的支持,從代碼量來(lái)講得不償失晋南。
以下是const的注意事項(xiàng)
const跟let特性一致的地方
- 塊級(jí)作用域
- 不提升
- 不可重復(fù)聲明(重復(fù)聲明跟再次賦值是兩碼事)
- 即使在全局作用域聲明惠猿,也不會(huì)是
window
對(duì)象的屬性
跟let特性的區(qū)別
就一條,let雖然不能重復(fù)聲明负间,但是可以無(wú)限制的賦值偶妖,但const只能初始化的時(shí)候賦值一次。
到底啥時(shí)候用let政溃,啥時(shí)候用const趾访?
一句話:const永遠(yuǎn)優(yōu)先原則。
- 在全局環(huán)境董虱,不應(yīng)該設(shè)置變量扼鞋,只應(yīng)設(shè)置常量》哂眨可能你有顧慮是我真的想讓一個(gè)量被修改云头,又真的想讓它在全局環(huán)境,怎么辦转锈?這種情況下盘寡,可以用這種方式:
const globelConst = {a: 1, b: 2}; // a和b的值確實(shí)是可以改變的,原因見(jiàn)下文
- 在局部環(huán)境撮慨,也應(yīng)該優(yōu)先考慮使用const竿痰,除非它真的需要改變值脆粥,那么就用let。
小心賦值復(fù)合類型數(shù)據(jù)
對(duì)于簡(jiǎn)單類型的數(shù)據(jù)(數(shù)值影涉、字符串变隔、布爾值),const的值是絕對(duì)不會(huì)變的蟹倾,因?yàn)樽兞恐赶虮4嬖谔囟▋?nèi)存地址的數(shù)據(jù)匣缘。但對(duì)于復(fù)合類型的數(shù)據(jù)(主要是對(duì)象和數(shù)組),變量指向的是對(duì)應(yīng)內(nèi)存地址的指針鲜棠,const只能保證這個(gè)指針是唯一的指針肌厨,至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就完全不能控制了豁陆。因此柑爸,將一個(gè)數(shù)組或者對(duì)象聲明為常量必須非常小心。
如果想要真正凍結(jié)一個(gè)數(shù)組或者對(duì)象盒音,可以用Object.freeze方法表鳍。
const foo = Object.freeze({});
// 常規(guī)模式時(shí),下面一行不起作用祥诽;
// 嚴(yán)格模式時(shí)譬圣,該行會(huì)報(bào)錯(cuò)
// 總之是不能用
foo.prop = 123;
但是這個(gè)方法只能淺凍結(jié),如果想深凍結(jié)雄坪,就得寫個(gè)方法:
var deepFreeze = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
deepFreeze( obj[key] );
}
});
};