深入淺出 ES6:迭代器和 for-of 循環(huán)

如何循環(huán)一個(gè)數(shù)組堕担?20年前 JavaScript 誕生的時(shí)候溅话,你會(huì)這么寫(xiě):

for (var index = 0; index < myArray.length; index++) {
  console.log(myArray[index]);
}

ES5 之后菌瘫,可以使用內(nèi)置的 forEach 方法:

myArray.forEach(function (value) {
  console.log(value);
});

這樣就稍微短了點(diǎn)了熊户,但是仍然有一個(gè)小的缺點(diǎn):無(wú)法使用 break 語(yǔ)句跳出循環(huán),或者使用 return 從函數(shù)體內(nèi)返回验懊。

要是有可以用 for 循環(huán)的語(yǔ)法遍歷數(shù)組的所有元素擅羞,那該多好。

那么for-in 循環(huán)如何义图?

for (var index in myArray) { // 不要真的這樣寫(xiě)
  console.log(myArray[index]);
}

這樣寫(xiě)有以下幾個(gè)問(wèn)題:

  • 代碼中賦值為index的值是字符串"0", "1","2"等祟滴,而不是真是的數(shù)字。由于你不想要碰到字符串計(jì)算("2" + 1 == "21")的狀況歌溉,這對(duì)于編程而言是極其不方便的。
  • 循環(huán)體不僅僅會(huì)遍歷數(shù)組元素,還會(huì)遍歷任意其他的自定義添加的屬性痛垛。例如草慧,如果數(shù)組包含了一個(gè)不能枚舉的屬性 myArray.name,那么這次循環(huán)就會(huì)在 index == "name" 的時(shí)候額外執(zhí)行一遍匙头。甚至數(shù)組原型鏈上的屬性也都會(huì)被遍歷到漫谷。
  • 最讓人感到驚奇的是,在某些狀況下蹂析,這段代碼會(huì)以隨機(jī)順序循環(huán)數(shù)組元素舔示。

簡(jiǎn)而言之,for-in 循環(huán)在設(shè)計(jì)之初就是用于普通的以字符串為 key 值的對(duì)象的語(yǔ)法电抚,而不適用與數(shù)組惕稻。

強(qiáng)大的 for-of 循環(huán)

讓我們來(lái)看看 for-of 循環(huán):

for (var value of myArray) {
  console.log(value);
}

唔,上述代碼就是看起來(lái)并沒(méi)有很強(qiáng)大蝙叛,對(duì)嗎俺祠?好吧,我們之后會(huì)探索 for-of 循環(huán)隱藏的強(qiáng)大之處借帘。就現(xiàn)在而言蜘渣,只需要記住:

  • 這是最簡(jiǎn)潔肺然、直白的循環(huán)數(shù)組元素的方法
  • 可以避免所有 for-in 循環(huán)的陷阱
  • 不同于 forEach()蔫缸,可以使用 break, continuereturn

for-in 循環(huán)用以遍歷對(duì)象的屬性。

for-of 循環(huán)用以遍歷數(shù)據(jù) -- 就像數(shù)組中的值一樣

但這還不是所有的內(nèi)容际起。

其他集合也支持 for-of 進(jìn)行遍歷

for-of 循環(huán)不僅僅支持?jǐn)?shù)組的遍歷拾碌。同樣適用于很多類(lèi)似數(shù)組的對(duì)象,例如 DOM NodeList加叁。

它也支持字符串的遍歷倦沧,會(huì)把字符串作為一組 Unicode 的字符進(jìn)行遍歷:

for (var chr of "????") {
  alert(chr);
}

它也可以應(yīng)用于 Map 和 Set 對(duì)象(ES6中新增的數(shù)據(jù)結(jié)構(gòu),之后的文章中會(huì)提及)它匕。

例如展融,Set 對(duì)象可以有效的去重:

// 從一個(gè)單詞組成的數(shù)組聲明一個(gè)新的 Set
var uniqueWords = new Set(words);

然后你就輕松可以遍歷 Set 中的內(nèi)容了:

for (var word of uniqueWords) {
  console.log(word);
}

Map 則稍微有點(diǎn)不同:Map 中的數(shù)據(jù)是由鍵值對(duì)組成的,所以你需要使用將其中的鍵值解構(gòu)為兩個(gè)獨(dú)立的變量:

for (var [key, value] of phoneBookMap) {
  console.log(key + "'s phone number is: " + value);
}

解構(gòu) (Destructing) 也是 ES6 中的特性豫柬,同樣會(huì)在之后的文章中提及告希。

到現(xiàn)在為止,你可能已經(jīng)可以想象到: JavaScript 已經(jīng)有了一些新的集合類(lèi)型烧给,且在不久的將來(lái)會(huì)出現(xiàn)更多燕偶。而 for-of 則是被設(shè)計(jì)出來(lái)用以在這些集合上使用的循環(huán)語(yǔ)句。

for-of 并不適用于處理原有的原生對(duì)象础嫡,但是如果你想要遍歷對(duì)象的屬性指么,可以使用 for-in 或者內(nèi)置的 Object.keys()

// 輸出對(duì)象自身可以枚舉的值
for (var key of Object.keys(someObject)) {
  console.log(key + ": " + someObject[key]);
}

深入本質(zhì)

能工摹形酝惧,巧匠竊意。 -- 畢加索

ES6 中新增的特性都不是憑空出現(xiàn)的伯诬,大部分特性的優(yōu)點(diǎn)都已經(jīng)在其他的編程語(yǔ)言中被證實(shí)過(guò)晚唇。

例如 for-of 循環(huán)與 C++, Java, C#和 Python中的循環(huán)語(yǔ)句十分類(lèi)似。與這些語(yǔ)言一樣盗似,for-of 循環(huán)適用于語(yǔ)言本身以及標(biāo)準(zhǔn)庫(kù)中的不同的數(shù)據(jù)類(lèi)型哩陕。同時(shí)它也是這門(mén)語(yǔ)言的一個(gè)擴(kuò)展點(diǎn)。

如同其他語(yǔ)言中的 for/foreach 語(yǔ)句赫舒,for-of 通過(guò)方法調(diào)用來(lái)實(shí)現(xiàn)集合的遍歷悍及。數(shù)組、Maps接癌、Sets 以及其他我們討論過(guò)的對(duì)象之間有個(gè)共同點(diǎn):有迭代器方法心赶。

當(dāng)然,任何對(duì)象都可以添加迭代器方法扔涧。

就像你可以給任意對(duì)象添加 myObject.toString() 方法园担,使之可以將對(duì)象轉(zhuǎn)換為字符串,你可以將 myObject[Symbol.iterator]() 方法添加到任意對(duì)象枯夜,這樣對(duì)象就可以被遍歷了弯汰。

例如,假設(shè)你正在使用 jQuery湖雹,盡管你非常喜歡 .each() 方法咏闪,但是你仍希望 jQuery 對(duì)象可以支持 for-of 循環(huán)。以下就是實(shí)現(xiàn)的方法:

//  jQuery 對(duì)象與數(shù)組類(lèi)似
// 賦予他們和數(shù)組一樣的迭代器方法
jQuery.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

我知道你在想什么摔吏。[Symbol.iterator] 語(yǔ)法看起來(lái)非常奇怪鸽嫂。這段代碼到底做了什么事?它在此處通過(guò) Symbol 處理了方法名征讲。標(biāo)準(zhǔn)委員會(huì)稱(chēng)之為 .iterator()据某,也許你已經(jīng)有些代碼包含了 .iterator() 方法,這確實(shí)會(huì)讓你感到困惑诗箍。因此標(biāo)準(zhǔn)使用了symbol 作為這個(gè)方法的名稱(chēng)而不是一個(gè)字符串癣籽。

Symbols 是 ES6 中的新特性 -- 這將會(huì)在以后的博文中介紹。就現(xiàn)在而言滤祖,你只需知道標(biāo)準(zhǔn)可以定義一種全新的 symbol 筷狼,例如 Symbol.iterator,這可以保證與現(xiàn)有的代碼不沖突匠童。這樣做的代價(jià)就是語(yǔ)法看起來(lái)略顯奇怪埂材。但是這僅僅是這種語(yǔ)法帶來(lái)的那么多特性與向后兼容性所造成的輕微代價(jià)。

一個(gè)包含[Symbol.iterator]() 方法的對(duì)象被稱(chēng)之為可迭代汤求。在接下來(lái)的文章中俏险,我們可以看到可迭代對(duì)象的概念貫穿了JavaScript 語(yǔ)言严拒,不僅僅是 for-in ,還有 MapSet 的構(gòu)造函數(shù)竖独,解構(gòu)賦值以及一種新的展開(kāi)操作符糙俗。

迭代器對(duì)象

現(xiàn)在,你無(wú)須自己從頭實(shí)現(xiàn)一個(gè)迭代器對(duì)象预鬓。但是從本文的完整性的角度而言,我們需要了解一下迭代器對(duì)象赊颠。(如果你跳過(guò)了這一整節(jié)內(nèi)容格二,你會(huì)錯(cuò)過(guò)很多精彩的技術(shù)細(xì)節(jié))

for-of 循環(huán)在集合中先調(diào)用 [Symbol.iterator]() 方法。然后返回一個(gè)新的迭代器對(duì)象竣蹦。一個(gè)迭代器對(duì)象可以是任意包含 .next() 方法的對(duì)象顶猜;for-of 會(huì)在循環(huán)的過(guò)程中重復(fù)調(diào)用這個(gè)方法。例如痘括,以下是一個(gè)我所能想到的最簡(jiǎn)單的迭代器對(duì)象:

var zeroesForeverIterator = {
  [Symbol.iterator]: function () {
    return this;
  },
  next: function () {
    return {done: false, value: 0};
  }
};

每當(dāng)這個(gè) .next() 方法被調(diào)用的時(shí)候长窄,它都會(huì)返回相同的結(jié)果,以此告知 for-of 循環(huán)(a) 還沒(méi)有結(jié)束迭代纲菌;(b) 下一個(gè)值是 o挠日。這也就意味著 (value of zeroesForeverIterator){} 是一個(gè)無(wú)限循環(huán)體。當(dāng)然翰舌,一個(gè)典型的迭代器不會(huì)如此簡(jiǎn)單嚣潜。

JavaScript 中的迭代器及它的 .done.value 屬性在表面上看起來(lái)和其他語(yǔ)言中的迭代器的不同。在 Java 中椅贱,迭代器有兩個(gè)獨(dú)立的 .hasNext().next() 方法懂算。在 Python 中,只有一個(gè) .next() 方法庇麦,在沒(méi)有更多值的時(shí)候會(huì)拋出 StopIteration 異常计技。但是這三種設(shè)計(jì)從根本上而言,都返回了一樣的信息山橄。

一個(gè)迭代器對(duì)象也可以實(shí)現(xiàn)可選的 .return().throw(exc) 方法垮媒。for-of 會(huì)在循環(huán)過(guò)早結(jié)束的時(shí)候調(diào)用 .return() 方法,這可能是因?yàn)楫惓<莸āreak 或者 return 語(yǔ)句涣澡。迭代器可以實(shí)現(xiàn) .return(),如果它需要做一些清理或者釋放正在使用的資源的操作丧诺。大多數(shù)迭代器都不需要去實(shí)現(xiàn)這個(gè)方法入桂。.throw(exc) 則應(yīng)用于更特殊的情況:for-of 永遠(yuǎn)不會(huì)調(diào)用它。我們會(huì)在之后的文章中詳細(xì)討論驳阎。

既然我們已經(jīng)了解了所有的細(xì)節(jié)抗愁,現(xiàn)在來(lái)使用一個(gè)簡(jiǎn)單的 for-of 循環(huán)馁蒂,然后用底層的方法重寫(xiě)之:

首先,是一個(gè) for-of 循環(huán):

for (VAR of ITERABLE) {
  STATEMENTS
}

接下來(lái)是一個(gè)大致等價(jià)的實(shí)現(xiàn)蜘腌,使用了底層的方法和一些臨時(shí)變量:

var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
  VAR = $result.value;
  STATEMENTS
  $result = $iterator.next();
}

這段代碼沒(méi)有展示 .return() 是如何被處理的沫屡。我們可以在代碼中添加上去,但是我覺(jué)得這樣反而會(huì)使其變得晦澀撮珠。for-of 使用起來(lái)非常簡(jiǎn)單沮脖,但是背后卻有很多值得學(xué)習(xí)的地方。

什么時(shí)候可以開(kāi)始使用這一特性芯急?

所有當(dāng)前的 Firefox 版本都已經(jīng)支持了 for-of 循環(huán)勺届。在 Chrome 中使用需要到 chrome://flags 中開(kāi)啟 "Experimental JavaScript"。微軟的 Spartan 瀏覽器中也可以使用娶耍,不過(guò)沒(méi)有在已經(jīng)發(fā)布的 IE 中被支持免姿。如果你想要使用這種新的語(yǔ)法,但是想要支持 IE 和 Safari榕酒,可以使用像 Babel 或者 Google 的 Traceur 編譯器將你的 ES6 代碼轉(zhuǎn)換為 Web 友好的 ES5 代碼胚膊。

而在服務(wù)端,則不需要編譯器的幫助 -- 現(xiàn)在你就可以在 io.js 中使用 for-of (而在 Node中想鹰,需要加入 --harmony 選項(xiàng))紊婉。


這個(gè)系列的譯文與原文一致遵守CC BY-SA 3.0 協(xié)議。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杖挣,一起剝皮案震驚了整個(gè)濱河市肩榕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惩妇,老刑警劉巖株汉,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異歌殃,居然都是意外死亡乔妈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)氓皱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)路召,“玉大人,你說(shuō)我怎么就攤上這事波材」傻” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵廷区,是天一觀的道長(zhǎng)唯灵。 經(jīng)常有香客問(wèn)我,道長(zhǎng)隙轻,這世上最難降的妖魔是什么埠帕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任垢揩,我火速辦了婚禮,結(jié)果婚禮上敛瓷,老公的妹妹穿的比我還像新娘叁巨。我一直安慰自己,他們只是感情好呐籽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布锋勺。 她就那樣靜靜地躺著,像睡著了一般狡蝶。 火紅的嫁衣襯著肌膚如雪宙刘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天牢酵,我揣著相機(jī)與錄音,去河邊找鬼衙猪。 笑死馍乙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的垫释。 我是一名探鬼主播丝格,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼棵譬!你這毒婦竟也來(lái)了显蝌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤订咸,失蹤者是張志新(化名)和其女友劉穎曼尊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體脏嚷,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骆撇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了父叙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片神郊。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖趾唱,靈堂內(nèi)的尸體忽然破棺而出涌乳,到底是詐尸還是另有隱情,我是刑警寧澤甜癞,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布夕晓,位于F島的核電站,受9級(jí)特大地震影響带欢,放射性物質(zhì)發(fā)生泄漏运授。R本人自食惡果不足惜烤惊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吁朦。 院中可真熱鬧柒室,春花似錦、人聲如沸逗宜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纺讲。三九已至擂仍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間熬甚,已是汗流浹背逢渔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乡括,地道東北人肃廓。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诲泌,于是被迫代替她去往敵國(guó)和親盲赊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容