飯飯的ES6完全筆記(1)——let和const
歡迎到我的博客下與我討論飯飯的ES6完全筆記(1)——let和const
之前對于JS的一些新規(guī)范總是只會用一些很常用的比如箭頭函數(shù)等,最近剛好重新系統(tǒng)學(xué)習(xí)一下ES6的拓展語法给猾,對學(xué)習(xí)過程做一些記錄
ES6介紹
ES6簡介
ECMAScript 6.0(以下簡稱ES6)是JavaScript語言的下一代標(biāo)準(zhǔn)娇唯,已經(jīng)在2015年6月正式發(fā)布了挤忙。它的目標(biāo),是使得JavaScript語言可以用來編寫復(fù)雜的大型應(yīng)用程序藤为,成為企業(yè)級開發(fā)語言今阳。
ECMAScript和JavaScript的關(guān)系
一個常見的問題是,ECMAScript和JavaScript到底是什么關(guān)系朴读?
要講清楚這個問題,需要回顧歷史走趋。1996年11月衅金,JavaScript的創(chuàng)造者Netscape公司,決定將JavaScript提交給國際標(biāo)準(zhǔn)化組織ECMA,希望這種語言能夠成為國際標(biāo)準(zhǔn)氮唯。次年鉴吹,ECMA發(fā)布262號標(biāo)準(zhǔn)文件(ECMA-262)的第一版,規(guī)定了瀏覽器腳本語言的標(biāo)準(zhǔn)惩琉,并將這種語言稱為ECMAScript豆励,這個版本就是1.0版。
該標(biāo)準(zhǔn)從一開始就是針對JavaScript語言制定的瞒渠,但是之所以不叫JavaScript良蒸,有兩個原因。一是商標(biāo)伍玖,Java是Sun公司的商標(biāo)嫩痰,根據(jù)授權(quán)協(xié)議,只有Netscape公司可以合法地使用JavaScript這個名字窍箍,且JavaScript本身也已經(jīng)被Netscape公司注冊為商標(biāo)串纺。二是想體現(xiàn)這門語言的制定者是ECMA,不是Netscape椰棘,這樣有利于保證這門語言的開放性和中立性造垛。
因此,ECMAScript和JavaScript的關(guān)系是晰搀,前者是后者的規(guī)格,后者是前者的一種實(shí)現(xiàn)(另外的ECMAScript方言還有Jscript和ActionScript)办斑。日常場合外恕,這兩個詞是可以互換的。
ES6與ECMAScript 2015的關(guān)系
媒體里面經(jīng)诚绯幔可以看到”ECMAScript 2015“這個詞鳞疲,它與ES6是什么關(guān)系呢?
2011年蠕蚜,ECMAScript 5.1版發(fā)布后尚洽,就開始制定6.0版了。因此靶累,”ES6”這個詞的原意腺毫,就是指JavaScript語言的下一個版本。
但是挣柬,因?yàn)檫@個版本引入的語法功能太多潮酒,而且制定過程當(dāng)中,還有很多組織和個人不斷提交新功能邪蛔。事情很快就變得清楚了急黎,不可能在一個版本里面包括所有將要引入的功能。常規(guī)的做法是先發(fā)布6.0版,過一段時間再發(fā)6.1版勃教,然后是6.2版淤击、6.3版等等。
但是故源,標(biāo)準(zhǔn)的制定者不想這樣做污抬。他們想讓標(biāo)準(zhǔn)的升級成為常規(guī)流程:任何人在任何時候,都可以向標(biāo)準(zhǔn)委員會提交新語法的提案心软,然后標(biāo)準(zhǔn)委員會每個月開一次會壕吹,評估這些提案是否可以接受,需要哪些改進(jìn)删铃。如果經(jīng)過多次會議以后耳贬,一個提案足夠成熟了,就可以正式進(jìn)入標(biāo)準(zhǔn)了猎唁。這就是說咒劲,標(biāo)準(zhǔn)的版本升級成為了一個不斷滾動的流程,每個月都會有變動诫隅。
標(biāo)準(zhǔn)委員會最終決定腐魂,標(biāo)準(zhǔn)在每年的6月份正式發(fā)布一次,作為當(dāng)年的正式版本逐纬。接下來的時間蛔屹,就在這個版本的基礎(chǔ)上做改動,直到下一年的6月份豁生,草案就自然變成了新一年的版本兔毒。這樣一來,就不需要以前的版本號了甸箱,只要用年份標(biāo)記就可以了育叁。
ES6的第一個版本,就這樣在2015年6月發(fā)布了芍殖,正式名稱就是《ECMAScript 2015標(biāo)準(zhǔn)》(簡稱ES2015)豪嗽。2016年6月,小幅修訂的《ECMAScript 2016標(biāo)準(zhǔn)》(簡稱ES2016)如期發(fā)布豌骏,這個版本可以看作是ES6.1版龟梦,因?yàn)閮烧叩牟町惙浅P。ㄖ恍略隽藬?shù)組實(shí)例的includes
方法和指數(shù)運(yùn)算符)肯适,基本上是同一個標(biāo)準(zhǔn)变秦。根據(jù)計劃,2017年6月將發(fā)布ES2017標(biāo)準(zhǔn)框舔。
因此蹦玫,ES6既是一個歷史名詞赎婚,也是一個泛指,含義是5.1版以后的JavaScript的下一代標(biāo)準(zhǔn)樱溉,涵蓋了ES2015挣输、ES2016、ES2017等等福贞,而ES2015則是正式名稱撩嚼,特指該年發(fā)布的正式版本的語言標(biāo)準(zhǔn)。本書中提到“ES6”的地方挖帘,一般是指ES2015標(biāo)準(zhǔn)完丽,但有時也是泛指“下一代JavaScript語言”。
ECMAScript的歷史
ES6從開始制定到最后發(fā)布拇舀,整整用了15年逻族。
前面提到,ECMAScript 1.0是1997年發(fā)布的骄崩,接下來的兩年聘鳞,連續(xù)發(fā)布了ECMAScript 2.0(1998年6月)和ECMAScript 3.0(1999年12月)。3.0版是一個巨大的成功要拂,在業(yè)界得到廣泛支持抠璃,成為通行標(biāo)準(zhǔn),奠定了JavaScript語言的基本語法脱惰,以后的版本完全繼承搏嗡。直到今天,初學(xué)者一開始學(xué)習(xí)JavaScript拉一,其實(shí)就是在學(xué)3.0版的語法彻况。
2000年,ECMAScript 4.0開始醞釀舅踪。這個版本最后沒有通過,但是它的大部分內(nèi)容被ES6繼承了良蛮。因此抽碌,ES6制定的起點(diǎn)其實(shí)是2000年。
為什么ES4沒有通過呢决瞳?因?yàn)檫@個版本太激進(jìn)了货徙,對ES3做了徹底升級,導(dǎo)致標(biāo)準(zhǔn)委員會的一些成員不愿意接受皮胡。ECMA的第39號技術(shù)專家委員會(Technical Committee 39痴颊,簡稱TC39)負(fù)責(zé)制訂ECMAScript標(biāo)準(zhǔn),成員包括Microsoft屡贺、Mozilla蠢棱、Google等大公司锌杀。
2007年10月,ECMAScript 4.0版草案發(fā)布泻仙,本來預(yù)計次年8月發(fā)布正式版本糕再。但是,各方對于是否通過這個標(biāo)準(zhǔn)玉转,發(fā)生了嚴(yán)重分歧突想。以Yahoo、Microsoft究抓、Google為首的大公司猾担,反對JavaScript的大幅升級,主張小幅改動刺下;以JavaScript創(chuàng)造者Brendan Eich為首的Mozilla公司绑嘹,則堅(jiān)持當(dāng)前的草案。
2008年7月怠李,由于對于下一個版本應(yīng)該包括哪些功能圾叼,各方分歧太大,爭論過于激烈捺癞,ECMA開會決定夷蚊,中止ECMAScript 4.0的開發(fā),將其中涉及現(xiàn)有功能改善的一小部分髓介,發(fā)布為ECMAScript 3.1惕鼓,而將其他激進(jìn)的設(shè)想擴(kuò)大范圍,放入以后的版本唐础,由于會議的氣氛箱歧,該版本的項(xiàng)目代號起名為Harmony(和諧)。會后不久一膨,ECMAScript 3.1就改名為ECMAScript 5呀邢。
2009年12月,ECMAScript 5.0版正式發(fā)布豹绪。Harmony項(xiàng)目則一分為二价淌,一些較為可行的設(shè)想定名為JavaScript.next繼續(xù)開發(fā),后來演變成ECMAScript 6瞒津;一些不是很成熟的設(shè)想蝉衣,則被視為JavaScript.next.next,在更遠(yuǎn)的將來再考慮推出巷蚪。TC39委員會的總體考慮是病毡,ES5與ES3基本保持兼容,較大的語法修正和新功能加入屁柏,將由JavaScript.next完成啦膜。當(dāng)時有送,JavaScript.next指的是ES6,第六版發(fā)布以后功戚,就指ES7娶眷。TC39的判斷是,ES5會在2013年的年中成為JavaScript開發(fā)的主流標(biāo)準(zhǔn)啸臀,并在此后五年中一直保持這個位置届宠。
2011年6月,ECMAscript 5.1版發(fā)布乘粒,并且成為ISO國際標(biāo)準(zhǔn)(ISO/IEC 16262:2011)豌注。
2013年3月,ECMAScript 6草案凍結(jié)灯萍,不再添加新功能轧铁。新的功能設(shè)想將被放到ECMAScript 7。
2013年12月旦棉,ECMAScript 6草案發(fā)布齿风。然后是12個月的討論期,聽取各方反饋绑洛。
2015年6月救斑,ECMAScript 6正式通過,成為國際標(biāo)準(zhǔn)真屯。從2000年算起脸候,這時已經(jīng)過去了15年。
兼容性
目前大部分瀏覽器已經(jīng)兼容ES6绑蔫,甚至在開發(fā)中像React這樣的框架已經(jīng)開始使用ES7的語法進(jìn)行開發(fā)运沦,截止2020年8月,市面上大部分瀏覽器的兼容性如下
腳本引擎 | 應(yīng)用程序 | ES5 | ES6 | ES7 | ES2016+ |
---|---|---|---|---|---|
Chakra | Microsoft Edge 18 | 100% | 96% | 100% | 48% |
SpiderMonkey | Firefox 67 | 100% | 98% | 100% | 83% |
Chrome V8 | Google Chrome 75配深、Opera 62 | 100% | 98% | 100% | 98% |
JavaScriptCore(Nitro) | Safari 12.1 | 99% | 99% | 100% | 87% |
Babel轉(zhuǎn)碼器
Babel是一個廣泛使用的ES6轉(zhuǎn)碼器携添,可以將ES6代碼轉(zhuǎn)為ES5代碼,從而在現(xiàn)有環(huán)境執(zhí)行篓叶。這意味著薪寓,你可以用ES6的方式編寫程序,又不用擔(dān)心現(xiàn)有環(huán)境是否支持澜共。
let和const
let
let
類似于var
,用于聲明變量锥腻,但不同于var
嗦董,let
聲明的變量只在自身所在代碼塊內(nèi)有效
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
常用于循環(huán)遍歷中,再也不用擔(dān)心循環(huán)變量沖突了
var a = [1 ,2 ,3]
for(let i = 0; i < 3; i++){
console.log(a[i] ++)
}
// 1
// 2
// 3
for(let i = 0; i < 3; i++){
console.log(a[i])
}
// 2
// 3
// 4
以下是一些注意事項(xiàng)
不存在變量提升
對于var
來說瘦黑,只要在腳本中聲明了變量京革,該變量就已經(jīng)存在奇唤。但對于let來說,只有在變量聲明之后才可以使用
// var 的情況
console.log(foo); // 輸出undefined
var foo = 2;
// let 的情況
console.log(bar); // 報錯ReferenceError
let bar = 2;
暫時性死區(qū)
ES6 明確規(guī)定匹摇,如果區(qū)塊中存在let
和const
命令咬扇,這個區(qū)塊對這些命令聲明的變量,從一開始就形成了封閉作用域廊勃。凡是在聲明之前就使用這些變量懈贺,就會報錯。
總之坡垫,在代碼塊內(nèi)梭灿,使用let
命令聲明變量之前,該變量都是不可用的冰悠。這在語法上堡妒,稱為“暫時性死區(qū)”(temporal dead zone,簡稱 TDZ)溉卓。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
不能重復(fù)聲明
// 報錯
function func() {
let a = 10;
var a = 1;
}
const
const 用于聲明一個只讀常量皮迟,聲明后便不得改變,這意味著聲明后需要立刻初始化桑寨。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const foo;
// SyntaxError: Missing initializer in const declaration
此外伏尼,const和let一樣,只在聲明的塊級作用域內(nèi)有效
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
同樣的西疤,const也存在不能變量提升烦粒,暫時性死區(qū),不能重復(fù)聲明等情況
常量對象陷阱
const實(shí)際保證的是該變量指向的地址內(nèi)容不能改動代赁,但對于復(fù)合型數(shù)據(jù)扰她,實(shí)際只是一個指向?qū)嶋H數(shù)據(jù)的指針。const只能保證這個指針是固定的芭碍,但不能保證指針指向數(shù)據(jù)是固定的徒役。因此將一個對象聲明為const要非常小心。
// 對象
const foo = {};
// 為 foo 添加一個屬性窖壕,可以成功
foo.prop = 123;
foo.prop // 123
// 將 foo 指向另一個對象忧勿,就會報錯
foo = {}; // TypeError: "foo" is read-only
// 數(shù)組
const a = [];
a.push('Hello'); // 可執(zhí)行
a.length = 0; // 可執(zhí)行
a = ['Dave']; // 報錯
如果真的想凍結(jié)一個對象≌胺恚可以使用Object.freeze
方法
const foo = Object.freeze({});
// 常規(guī)模式時鸳吸,下面一行不起作用;
// 嚴(yán)格模式時速勇,該行會報錯
foo.prop = 123;
塊級作用域
const和let實(shí)際上為 JavaScript 新增了塊級作用域晌砾, ES6允許作用域嵌套,內(nèi)層作用域可以重新定義外層作用域的變量
{{{{
{let insane = 'Hello World'}
console.log(insane); // 報錯
}}}};
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};
對于塊級作用域內(nèi)的函數(shù)烦磁,由于ES6中一些神奇的規(guī)定养匈,使得目前這個規(guī)則變得非常奇怪哼勇,所以應(yīng)該避免在塊級作用域內(nèi)聲明函數(shù),如果確實(shí)需要也應(yīng)該寫成表達(dá)式
// 塊級作用域內(nèi)部的函數(shù)聲明語句呕乎,建議不要使用
{
let a = 'secret';
function f() {
return a;
}
}
// 塊級作用域內(nèi)部积担,優(yōu)先使用函數(shù)表達(dá)式
{
let a = 'secret';
let f = function () {
return a;
};
}
還有一個需要注意的地方,es6的塊級作用域必須要大括號猬仁,沒有大括號JS會認(rèn)為不存在塊級作用域帝璧,所以有l(wèi)et和const等塊級作用域的塊即使只有一行也不能省略大括號
// 第一種寫法,報錯
if (true) let x = 1;
// 第二種寫法逐虚,不報錯
if (true) {
let x = 1;
}
頂層對象
頂層對象聋溜,是指在環(huán)境中的全局變量,在瀏覽器中一般指Windows
叭爱,在Node中指global
對象撮躁。需要注意的是,ES6為了改善JS編程中全局變量到處跑的狀況买雾,規(guī)定let和const所聲明的變量不屬于頂層對象的屬性把曼,另一方面,為了兼容原來的規(guī)定漓穿,var和function所聲明的變量仍然是頂層變量的屬性嗤军。因此,在之后的編程中我們應(yīng)該養(yǎng)成良好的習(xí)慣用let和const去替換var和直接聲明函數(shù)晃危,避免不必要的麻煩叙赚。
var a = 1;
// 如果在 Node 的 REPL 環(huán)境,可以寫成 global.a
// 或者采用通用方法僚饭,寫成 this.a
window.a // 1
let b = 1;
window.b // undefined
調(diào)用頂層對象
目前JS的頂層對象十分混亂
- 瀏覽器里面震叮,頂層對象是
window
,但 Node 和 Web Worker 沒有window
鳍鸵。 - 瀏覽器和 Web Worker 里面苇瓣,
self
也指向頂層對象,但是 Node 沒有self
偿乖。 - Node 里面击罪,頂層對象是
global
,但其他環(huán)境都不支持贪薪。
同一段代碼為了能夠在各種環(huán)境媳禁,都能取到頂層對象,現(xiàn)在一般是使用this
變量画切,但是有局限性竣稽。
- 前模塊,ES6 模塊中
this
返回的是undefined
。 - 函數(shù)里面的
this
丧枪,如果函數(shù)不是作為對象的方法運(yùn)行,而是單純作為函數(shù)運(yùn)行庞萍,this
會指向頂層對象拧烦。但是,嚴(yán)格模式下钝计,這時this
會返回undefined
恋博。 - 不管是嚴(yán)格模式,還是普通模式私恬,
new Function('return this')()
债沮,總是會返回全局對象。但是本鸣,如果瀏覽器用了 CSP(Content Security Policy疫衩,內(nèi)容安全策略),那么eval
荣德、new Function
這些方法都可能無法使用闷煤。
ES2020中,引入了globalThis
作為統(tǒng)一的頂層對象來進(jìn)行調(diào)用涮瞻。也就是說鲤拿,任何環(huán)境下,globalThis
都是存在的署咽,都可以從它拿到頂層對象近顷,指向全局環(huán)境下的this
。