【探秘ES6】系列专栏(二):迭代器和for-of循环
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
ES6作為新一代JavaScript標(biāo)準(zhǔn),即將與廣大前端開發(fā)者見面。為了讓大家對(duì)ES6的諸多新特性有更深入的了解,Mozilla Web開發(fā)者博客推出了《ES6 In Depth》系列文章。CSDN已獲授權(quán),將持續(xù)對(duì)該系列進(jìn)行翻譯,組織成【探秘ES6】系列專欄,供大家學(xué)習(xí)借鑒。本文為該系列的第二篇。?
你是如何遍歷數(shù)組中的元素的?20年前JavaScript剛進(jìn)入視野時(shí),你應(yīng)該是這樣寫的:
for?(var?index?=?0;?index?<?myArray.length;?index++)?{???console.log(myArray[index]);??? }直到ES5中原生JavaScript中添加了forEach方法:
語法上簡潔了一些,但是它有一個(gè)小小的不足:你不能用break語句跳出循環(huán)且不能在這個(gè)封閉的函數(shù)內(nèi)使用return語句。
如果有一個(gè)簡單的for-loop語法來遍歷數(shù)組就好了。
使用一個(gè)for-in循環(huán)怎么樣?
for?(var?index?in?myArray)?{?????//?don't?actually?do?this???console.log(myArray[index]);??? }我用幾個(gè)理由來說明這并不是一個(gè)好主意:
數(shù)組的索引值index是String類型的“0”,“1”,“2”等等,而不是Number類型。當(dāng)你進(jìn)行算術(shù)運(yùn)算時(shí)(“2”+1==“21”)也許并不是你期望的結(jié)果,所以運(yùn)算前需要類型轉(zhuǎn)換,這很不方便。
循環(huán)體不僅會(huì)遍歷數(shù)組的元素,甚至連expando屬性也遍歷出來了。舉個(gè)例子,如果你的myArray數(shù)組中有一個(gè)叫做name的屬性,遍歷時(shí)就將 index ==”name”也遍歷出來,這樣就多了一次執(zhí)行。即時(shí)這些屬性在數(shù)組的原型鏈上是可直接訪問的。
最讓人無語的是,在某些情況下,這段代碼在遍歷數(shù)組元素時(shí)順序是任意的。
總而言之,for-in語法是被設(shè)計(jì)來遍歷普通的“鍵值對(duì)”對(duì)象的,不適合用在數(shù)組上。
強(qiáng)大的for-of循環(huán)
還記得我上篇提到的ES6是向后兼容的嗎。即使在遍歷數(shù)組的時(shí)候,成千上萬的網(wǎng)站使用了for-in循環(huán)。所有“修復(fù)”for-in讓它更適用于數(shù)組是有必要的。ES6來解決這個(gè)問題的唯一途徑是新增一個(gè)新的遍歷語法。
新語法如下:
for?(var?value?of?myArray)?{???console.log(value);??? }恩?!從構(gòu)建上來說好像并沒什么改變,事實(shí)如此嗎?當(dāng)然不是,我們來看看for-of的葫蘆里究竟賣的什么藥。首先,只需要注意這幾點(diǎn):
這是目前遍歷數(shù)組最簡潔和直接的語法;
它避免了for-in的所有缺陷;
與forEach()不一樣,它支持break,continue和return。
for-in循環(huán)用于遍歷對(duì)象屬性。
for-of循環(huán)用于遍歷數(shù)據(jù)——比如數(shù)組中單值。
其它集合也支持for-of
for-of循環(huán)不僅僅是為遍歷數(shù)組而設(shè)計(jì)的。基本上所有類數(shù)組對(duì)象都適用,比如DOM NodeListS。
也能用在字符串上,它將字符串當(dāng)做一個(gè)Unicode字符序列:
?
它也能用在Map和Set對(duì)象上。
哦,不好意思,你沒聽說過Map和Set?沒關(guān)系,他們是出現(xiàn)在ES6中的新成員。有機(jī)會(huì)我們會(huì)寫個(gè)完整的關(guān)于它的文章。如果你使用過其它編程語言中的maps和sets,那么你也不會(huì)有陌生感。
例如,一個(gè)set對(duì)象使用于排除重復(fù)項(xiàng):
//?make?a?set?from?an?array?of?words??? var?uniqueWords?=?new?Set(words);如果你想遍歷你的set,很簡單:
for?(var?word?of?uniqueWords)?{???console.log(word);??}Map有一點(diǎn)不同:它里面的數(shù)據(jù)由鍵值對(duì)組成,所以你需要使用destructuring將“鍵”和“值”解構(gòu)為兩個(gè)獨(dú)立的變量:
for?(var?[key,?value]?of?phoneBookMap)?{???console.log(key?+?"'s?phone?number?is:?"?+?value);??? }Destructuring(解構(gòu))也是ES6的新特性,在未來博客中會(huì)有很多關(guān)于它的文章。
目前為止,你可以這樣理解:JS已經(jīng)有了幾個(gè)不同的集合類,而且更多的集合類正在被添加進(jìn)來。for-of循環(huán)語句的設(shè)計(jì)初衷就是適用于所有這些集合類。
for-of并不能用于普通的舊對(duì)象。如果你想要遍歷對(duì)象的所有屬性,可以使用for-in,也可以通過Object.keys(object)將對(duì)象的所有屬性以數(shù)組形式返回后再使用for-of。?
//?dump?an?object's?own?enumerable?properties?to?the?console???for?(var?key?of?Object.keys(someObject))?{??? console.log(key?+?":?"?+?someObject[key]);??? }深入理解
“能工摹形,巧匠竊意。”——巴勃羅·畢加索
JavaScript在ES6中所新增的特性并不是憑空而來,大多數(shù)都是借鑒于其它優(yōu)秀的語言。
以for-of循環(huán)偽例,與C++、Java、C#和Python的循環(huán)語句非常類似。和它們一樣,支持該種語言提供的多種數(shù)據(jù)解構(gòu)和標(biāo)準(zhǔn)庫。但是它也是該種語言的一個(gè)擴(kuò)展點(diǎn)。
就像for/foreach語句在其它語言中一樣,for-of的執(zhí)行完全靠方法調(diào)用。像Arrays,Maps,Sets等我們提到過的對(duì)象都有一個(gè)共同點(diǎn)就是它們都有一個(gè)遍歷的方法。
其它類型的對(duì)象也都可有一個(gè)遍歷方法:任何對(duì)象都可以。
就像你可以對(duì)任何一個(gè)對(duì)象添加方法myObject.toString()讓JS知道如何將對(duì)象轉(zhuǎn)換為字符串一樣,你也可以對(duì)任何對(duì)象添加方法myObject.toString()來告訴JS如何遍歷這個(gè)對(duì)象。
例如,假設(shè)你使用的jQuery,盡管喜歡使用.each(),那你也會(huì)喜歡上在jQuery對(duì)象中使用for-of。請(qǐng)看下面這個(gè)例子:
//?Since?jQuery?objects?are?array-like,??? //?give?them?the?same?iterator?method?Arrays?have??jQuery.prototype[Symbol.iterator]?=??? Array.prototype[Symbol.iterator];好吧,我知道你會(huì)覺得[Symbol.iterator] 這樣的語法看起來很奇怪。它是怎么執(zhí)行的呢?使用方法名就可以了。標(biāo)準(zhǔn)委員會(huì)剛剛將這個(gè)方法命名為.iterator(),但是你已存在的代碼中可能已經(jīng)有了叫做.iterator的方法,那會(huì)造成命名沖突,讓人傻傻分不清。因此所有標(biāo)準(zhǔn)庫將其封裝進(jìn)了symbol,而不是使用簡單的用字符串來直接命名。
Symbols是ES6的新特性,我們將在以后的博客中討論它。目前,你所需要知道的是現(xiàn)在標(biāo)準(zhǔn)定義了一個(gè)全新的symbol,比如Symbol.iterator,為了保證與已存在的代碼不存在命名沖突,所以這個(gè)代價(jià)就是語法看起來有點(diǎn)奇怪。
為了這個(gè)優(yōu)秀的新特性的向后兼容性,這點(diǎn)小代價(jià)也就微不足道了。
迭代器對(duì)象
從現(xiàn)在開始你再也沒有必要為自己寫一個(gè)迭代器對(duì)象了,這個(gè)我們?cè)谙缕恼轮性賮碛懻摗5浅鲇谕暾缘目紤],讓我們先來看看一個(gè)迭代器對(duì)象是什么樣子的。(如果你跳過這一節(jié),你會(huì)錯(cuò)過很多有趣的技術(shù)細(xì)節(jié)喲)。
for-of循環(huán)開始于對(duì)集合的[Symbol.iterator]()方法的調(diào)用。它會(huì)返回一個(gè)新的迭代器對(duì)象。任意一個(gè)有.next()方法的對(duì)象都可以被稱作迭代器對(duì)象;每次執(zhí)行進(jìn)入循環(huán)時(shí),for-of方法將會(huì)用.next()方法。例如,下面是一個(gè)我所能想到的最簡單的迭代器構(gòu)造:
var?zeroesForeverIterator?=?{??[Symbol.iterator]:?function?()?{??return?this;???},???next:?function?()?{???return?{done:?false,?value:?0};???}??};每次當(dāng).next()方法被調(diào)用的時(shí)候,它會(huì)返回相同的結(jié)果,告訴for-of循環(huán):(1)我們還沒結(jié)束迭代;(2)下一個(gè)值是0。這意味著,(value of zeroesForeverIterator) {}將是一個(gè)無線循環(huán)。當(dāng)然,一個(gè)真正的迭代器并不會(huì)這么簡單。
迭代器的設(shè)計(jì),伴隨著.done和.value屬性,從表面上來看似乎和其它語言中的迭代器不太一樣。在Java中,迭代器將.hasNext()和.next()區(qū)分為兩個(gè)方法。在Python中,它只有一個(gè).next()方法,當(dāng)沒有下一個(gè)值時(shí)會(huì)拋出StopIteration 。但是從根本上來說,這三種方法返回同樣的信息。
迭代器也可以實(shí)現(xiàn)一些可選方法,比如.return()和throw(ext)。在for-of循環(huán)中,當(dāng)遇到異常或者break和return語句時(shí)可以調(diào)用.return()方法提前退出循環(huán)。迭代器可以通過實(shí)現(xiàn).return()方法來清空變量或釋放當(dāng)前資源,大多數(shù)迭代器對(duì)象是使用不到這一點(diǎn)的。.throw(exc)是一個(gè)特殊的例子:for-of完全使用不到它,我們下次再來討論。
現(xiàn)在我們已經(jīng)了解了所有的基本細(xì)節(jié),我們可以寫一個(gè)簡單的循環(huán)并重寫它的底層方法調(diào)用部分。
先寫一個(gè)for-of循環(huán):
for?(VAR?of?ITERABLE)?{???STATEMENTS??? }下面這段代碼使用簡單的底層方法和幾個(gè)簡單的變量來實(shí)現(xiàn)同樣的功能:
var?$iterator?=?ITERABLE[Symbol.iterator]();?? var?$result?=?$iterator.next();?? while?(!$result.done)?{??VAR?=?$result.value;??STATEMENTS??$result?=?$iterator.next();?? }這段代碼并沒有體現(xiàn)出.return()操作。我們可以添加進(jìn)來,但是我認(rèn)為認(rèn)清它的執(zhí)行過程比闡明它更重要。for-of的使用起來很簡單,但有很多看不見幕后的工作。
我什么時(shí)候才能使用它?
當(dāng)前所有的Firefox releases版本都支持for-of循環(huán)。如果你想在Chrome中使用,到chrome://flags設(shè)置“Experimental JavaScript”為“開啟”即可。微軟的Spartan瀏覽器支持它,但是IE不支持。如果你想要在Web中使用這些新語法且不用考慮支持IE和Safari,你可以使用Babel或者谷歌的Traceur這樣的編譯器將你的ES6代碼轉(zhuǎn)換成兼容性友好的ES5。
在服務(wù)端,你不需要一個(gè)編譯器——你可以在io.js(基于Node,是一個(gè)不錯(cuò)的選擇)中使用for-of。
(更新:在Chrome中默認(rèn)是禁用的,這個(gè)被我忽視掉了,感謝Oleg 指出。)
講完啦!
我們今天的計(jì)劃都完成了,但是我們對(duì)for-of循環(huán)的學(xué)習(xí)還沒結(jié)束。
ES6中還有一個(gè)和for-of完美結(jié)合的新對(duì)象。我之所以沒提到它是因?yàn)樗俏覀兿麓蔚闹黝}。
我認(rèn)為它是ES6中最神奇的新特性。如果你之前沒在像Python和C#這樣的語言中使用過它,一開始它可能會(huì)讓你感到難以置信。無論在客戶端還是服務(wù)端,這是寫一個(gè)構(gòu)造器最簡單方法,對(duì)于重構(gòu)很有用,它有可能會(huì)改變我們寫異步代碼的方式習(xí)慣。
下次將一起深入ES6的Generators 。(譯者:向渝 責(zé)編:陳秋歌)
原文鏈接:ES6 In Depth: Iterators and the for-of loop
本譯文遵循Creative Commons Attribution Share-Alike License v3.0?
轉(zhuǎn)載于:https://my.oschina.net/1pei/blog/520584
總結(jié)
以上是生活随笔為你收集整理的【探秘ES6】系列专栏(二):迭代器和for-of循环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tomcat配置优化
- 下一篇: 大道至简第四章阅读笔记