詳解ES6之用let聲明變量以及l(fā)et loop機(jī)制
在上一篇對js閉包的理解那篇文章中,我提到過ES6之前,JavaScript這門語言沒有塊級作用域,但是在ES6中,加入了let命令,用let聲明的變量僅僅可以在其所在的塊級作用域中使用。
相比用var聲明,let聲明的特點(diǎn)
1.let聲明的變量僅在該變量所在的作用域有效
for(let i=0;i<5;i++){} console.log(i); //報(bào)錯(cuò) for(var i=0;i<5;i++){} console.log(i); //i=5;
2.不存在變量提升
先解釋下變量提升:變量提升就是變量聲明語句都會(huì)被提到所在作用域開始處,就是經(jīng)過變量提升后,代碼的實(shí)際執(zhí)行順序會(huì)與你所寫的順序不同。
console.log(i); var i=2;
因?yàn)樽兞刻嵘?,所以上述代碼不會(huì)報(bào)錯(cuò),只會(huì)輸出undefined。
但是要注意:變量提升只是提升聲明語句,而不提升賦值語句
所以上述語句經(jīng)過提升之后,實(shí)際的代碼執(zhí)行順序是
var i; console.log(i); i=2;
再來看道經(jīng)典試題
var name = 'World!'; (function () { if (typeof name === 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); }else{ console.log('Hello ' + name); } })();
請問最終console的結(jié)果是什么?
如果對變量提升一無所知,會(huì)很容易的得出hello world的結(jié)果。
我對變量提升略知一二,得出的結(jié)果是hello Jack.
但是正確結(jié)果是Goodbye Jack. 做錯(cuò)的原因就是我沒有注意到變量提升僅僅是聲明提升。
3.不可以在同一作用域內(nèi)重復(fù)聲明
function temp(){ let a; let b; } //報(bào)錯(cuò)
4.暫時(shí)性死區(qū)
var tmp="123"; if(true){ tmp="abc"; let tmp; }
定義(出自ES6標(biāo)準(zhǔn)入門一書):在代碼塊內(nèi),使用let變量聲明之前,該變量是不可以用的。這在語法上稱為暫時(shí)性死區(qū)。
但是如果去掉上述let tmp;這句,代碼是不會(huì)錯(cuò)的,只是這時(shí)的tmp即外部的全局變量,但是你如果要聲明一個(gè)與外部變量同名的局部變量,那么你就要放在這個(gè)同名變量使用之前,否則就會(huì)報(bào)錯(cuò)。
說一說(let i=0;i<5;i++)的運(yùn)行機(jī)制
不好意思,我們還得拿閉包來說事。
var a=[]; for(var i=0;i<5;i++){ a[i]=function(){ console.log(i); } }
然后,結(jié)果i并非我們想象得那樣,按照0,1,2...的順序打印,而都是打印5,在上一篇文章理解js閉包中,我們已經(jīng)知道如何用閉包解決這個(gè)問題。但是呢,這還有一種更簡單的解法,如下
for(let i=0;i<5;i++){ a[i]=function(){ console.log(i); } }
對,就是這么簡單,用一個(gè)let代替var,就搞定了。但是為什么呢?說實(shí)話,閉包我能想得通,但是用let真心想不通為什么可以解決這個(gè)問題。問過一些人,但是他們只是很糊弄得說,let是塊級作用域。我覺得他們壓根就沒有搞懂,我覺得這里絕對有蹊蹺,僅僅憑let是塊級作用域并不能解釋的通,一定有其他機(jī)制。
我的疑惑:就算let是塊級作用域,可是在函數(shù)里,我們沒有定義新的i,所以你訪問i,其實(shí)都在訪問父級作用域,對 let i=0;使得i只能在for循環(huán)的{}內(nèi)使用,但是當(dāng)你調(diào)用ai的時(shí)候,不管i是多少,它們都應(yīng)該是訪問的是同一個(gè)i,怎么會(huì)出現(xiàn)0,1,2,3,4呢?
百度一下,果然不出我所料,阮一峰大神給出的解釋是,對每一次循環(huán),都是產(chǎn)生不同的i,所以5次循環(huán),產(chǎn)生了5個(gè)塊級作用域,而不同的塊級作用域間實(shí)現(xiàn)i++是通過js引擎記住上一個(gè)i值實(shí)現(xiàn)的。這樣一來,ai調(diào)用時(shí),就會(huì)訪問到與自己對應(yīng)的i,而不是同一個(gè)i.
所以,let可以得到閉包的效果是因?yàn)閘et具有塊級作用域,與let loop特殊的機(jī)制。
起初,我對這個(gè)結(jié)果不太相信,因?yàn)楹椭庇X不太相符,感覺5次循環(huán),產(chǎn)生5個(gè)i,和自己以前的認(rèn)知不大相同啊。然后我有有了新的發(fā)現(xiàn)
第一個(gè)發(fā)現(xiàn):
for(let i=0;i<5;i++){ let i="abc"; console.log(i); }
我吃驚的是竟然沒有報(bào)錯(cuò)!說好的let不可以重復(fù)聲明呢?這充分證明了let i=0;與let i="abc"不在同一個(gè)作用域。
第二個(gè)發(fā)現(xiàn):可以通過babel將ES6代碼轉(zhuǎn)為ES5,看看上述代碼是如何實(shí)現(xiàn)的?
先創(chuàng)建一個(gè)loop函數(shù),最終在循環(huán)的時(shí)候調(diào)用loop函數(shù)并將i作為參數(shù)傳入,這樣便會(huì)形成5個(gè)不同的副本。這也證明了用let代替閉包,關(guān)鍵是形成了5個(gè)不同的塊級作用域。
最后,ES6只是一種規(guī)范,比如說let的規(guī)范,但實(shí)現(xiàn)該規(guī)范的是JS引擎,如babel對上述代碼的轉(zhuǎn)換便體現(xiàn)出了babel對let loop的實(shí)現(xiàn)。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js 數(shù)組隨機(jī)字符串(廣告不重復(fù))
今天一個(gè)網(wǎng)友想讓他的廣告隨機(jī)顯示,每次刷新廣告的內(nèi)容都不一樣,經(jīng)過參考源碼網(wǎng)站分析就是通過下面代碼實(shí)現(xiàn),特分享下方便需要的朋友2013-08-08微信小程序網(wǎng)絡(luò)數(shù)據(jù)請求服務(wù)實(shí)現(xiàn)詳解
這篇文章主要介紹了微信小程序網(wǎng)絡(luò)數(shù)據(jù)請求服務(wù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-10-10將CKfinder整合進(jìn)CKEditor3.0的新方法
最新發(fā)布的CKFinder 1.4版 已經(jīng)提供了對CKEditor3.0的支持2010-01-01ES6 javascript中class類的get與set用法實(shí)例分析
這篇文章主要介紹了ES6 javascript中class類的get與set用法,結(jié)合具體實(shí)例形式分析了ES6中類的get與set關(guān)鍵字使用方法,需要的朋友可以參考下2017-10-10JS設(shè)計(jì)模式之?dāng)?shù)據(jù)訪問對象模式的實(shí)例講解
下面小編就為大家?guī)硪黄狫S設(shè)計(jì)模式之?dāng)?shù)據(jù)訪問對象模式的實(shí)例講解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09JavaScript forEach的幾種用法小結(jié)
forEach()是JavaScript中一個(gè)常用的方法,用于遍歷數(shù)組或類數(shù)組對象中的每個(gè)元素,本文就來介紹一下JavaScript forEach的幾種用法小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11基于leaflet.js實(shí)現(xiàn)修改地圖主題樣式的流程分析
這篇文章主要介紹了基于leaflet.js實(shí)現(xiàn)修改地圖主題樣式的流程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05