你是如何遍歷數(shù)組中的元素的? 二十年前,當(dāng) JavaScript 被發(fā)布的時(shí)候,你可能是這么做的:
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è)小缺陷:你不能通過使用一個(gè) break 語(yǔ)句以退出循環(huán)或者使用 return 語(yǔ)句從封閉函數(shù)中返回。
如果在數(shù)組元素上只有一個(gè) for 循環(huán)的話肯定是很好的。
但是,如果是一個(gè) for-in 循環(huán)的話,那是怎樣的呢?
for (var index in myArray) { // don't actually do this
console.log(myArray[index]);
}
這將是一個(gè)很糟糕的想法,原因如下:
總之,for-in 被設(shè)計(jì)用來與以字符串作為鍵的普通的舊的對(duì)象工作。對(duì)于數(shù)組而言,它不是那么有效。
記得上周我承諾 ES6 不會(huì)破壞你已經(jīng)寫好的代碼。 那么,數(shù)以百萬計(jì)的網(wǎng)站依賴于 for-in-yes 的語(yǔ)義,甚至其在數(shù)組上的用法。 因此,“修復(fù)” for-in 在數(shù)組中的用法是沒有任何問題的。 對(duì)于 ES6,唯一重要的改進(jìn)就是添加新的語(yǔ)法。
在這里,它是:
for (var value of myArray) {
console.log(value);
}
嗯,這看起來并沒有很令人印象深刻,不是嗎?好了,我們將看到 for-of 是否有些巧妙的技巧。 現(xiàn)在,只需要注意的是:
for-in 循環(huán)是在對(duì)象的屬性上進(jìn)行循環(huán)。
for-of 循環(huán)是在數(shù)據(jù)上進(jìn)行循環(huán),如數(shù)組中的值。
但是這些并不是全部。
for-of 不僅僅在數(shù)組上使用。其也在大多是與數(shù)組類似的對(duì)象上使用,比如 DOM NodeListS。
其也適用于在字符串,其將字符串作為 Unicode 字符序列進(jìn)行處理:
for (var chr of "http://wiki.jikexueyuan.com/project/es-six-deeply/images/3.png" alt="" />http://wiki.jikexueyuan.com/project/es-six-deeply/images/4.png" alt="" /> "){
alert(chr);
}
其也適用于 Map 以及 Set 對(duì)象。
可能你還沒有聽說過 M 哦,我很抱歉 ap 和 Set 對(duì)象。 好吧,他們是新的東西,是在 ES6 中才出現(xiàn)的。 我們將在后面找時(shí)間給他們做一個(gè)整體的介紹。如果你已經(jīng)在其他語(yǔ)言中使用過 map 和 set 了,這應(yīng)該不是一個(gè)很大的驚喜了。
舉例來說, 一個(gè) Set 對(duì)象在消除重復(fù)的時(shí)候很好用:
// 從一個(gè)單詞數(shù)組中生成一個(gè)集合。
var uniqueWords = new Set(words);
一旦你獲得了一個(gè)集合,你可能愿意遍歷它的內(nèi)容。簡(jiǎn)單例子如下:
for (var word of uniqueWords) {
console.log(word);
}
一個(gè) Map 有略微的不同: 其數(shù)據(jù)是由一個(gè)鍵-值對(duì)組成。所以,你要使用解構(gòu)的方法來解壓鍵和值以使得他們成為兩個(gè)獨(dú)立的變量:
for (var [key, value] of phoneBookMap) {
console.log(key + "'s phone number is: " + value);
}
解構(gòu)也是 ES6 的一個(gè)新的功能。其也是我們?cè)诓┛椭袑⒁懻摰囊淮笤掝}。我應(yīng)該要將這些都記錄下來。
到現(xiàn)在為止,你應(yīng)該獲得這樣的直觀感受:JS 已經(jīng)有了相當(dāng)多的不同的集合類, 甚至更多的還在開發(fā)中。for-of 被設(shè)計(jì)為你使用的循環(huán)語(yǔ)句中的主力循環(huán)語(yǔ)句。
for-of 在普通的舊的對(duì)象上不能正常工作,但是如果你想要在一個(gè)對(duì)象的屬性上進(jìn)行循環(huán)的話,你可以使用 for–in ( 本來就是它的功能 ) 或者內(nèi)置的 Object.keys( ) :
// 將對(duì)象的自己的枚舉屬性轉(zhuǎn)儲(chǔ)到控制臺(tái)
for (var key of Object.keys(someObject)) {
console.log(key + ": " + someObject[key]);
}
“能工摹形,巧匠竊意。”—— 巴勃羅·畢加索
ES6 的一個(gè)正火熱的主題是:被添加到語(yǔ)言的新功能的推出毫無進(jìn)展。新功能的大多數(shù)都已經(jīng)在其他語(yǔ)言中被嘗試并被證實(shí)為有效的。
for-of 循環(huán),在其他語(yǔ)言也有類似的循環(huán)語(yǔ)句,比如,C++, Java, C#, 和 Python。和這些語(yǔ)言一樣,其工作基于語(yǔ)言提供的幾種不同的數(shù)據(jù)結(jié)構(gòu)以及其標(biāo)準(zhǔn)庫(kù)。但是,這仍然是語(yǔ)言的一個(gè)拓展點(diǎn)。
就像在其他語(yǔ)言中的 for/foreach 語(yǔ)句,for-of 完全在方法調(diào)用方面起作用。我們經(jīng)常談?wù)摰?,比如?shù)組,map,集合以及其他的一些對(duì)象,他們都有一個(gè)共同點(diǎn),即他們都有一個(gè)迭代的方法。
同時(shí),還存在另一種對(duì)象,它也有一個(gè)迭代的方法:任何你想要的對(duì)象。
正如你可以對(duì)任何一個(gè)對(duì)象添加一個(gè) myObject.toString( ) 方法后,JS 就 突然就知道了應(yīng)該如何將該對(duì)象轉(zhuǎn)換為一個(gè)字符串一樣。你可以對(duì)任何一個(gè)對(duì)象添加一個(gè) myObject[Symbol.iterator]( )方法,JS 突然就知道了應(yīng)該如何在該對(duì)象上進(jìn)行循環(huán)迭代。
舉例來說,假設(shè)你正在使用 jQuery ,雖然你非常喜歡 .each( ) ,但是你也想讓 jQuery 對(duì)象與 for-of 很好的一起工作。下述就是怎樣去讓這兩個(gè)很好的一起工作:
// 既然 jQuery 對(duì)象和數(shù)組比較類似,
// 將數(shù)組的迭代方法用在 jQuery 對(duì)象上。
jQuery.prototype[Symbol.iterator] =
Array.prototype[Symbol.iterator];
好吧,我知道你在想什么。這 [Symbol.iterator] 語(yǔ)法看上去似乎不可思議。這到底是怎么回事呢?我們從方法的名稱開始說起。標(biāo)準(zhǔn)委員會(huì)可以只稱這種方法為 .iterator( )。但是,你現(xiàn)有的代碼可能已經(jīng)有一些對(duì)象有 .iterator( ) 方法,這可能會(huì)使事情變得相當(dāng)混亂。所以標(biāo)準(zhǔn)委員會(huì)使用一個(gè)符號(hào),而不是字符串,作為此方法的名稱。
符號(hào)在 ES6 中也是新的。隨后,我們會(huì)告訴你關(guān)于他們的全部?jī)?nèi)容——你猜對(duì)了——在未來的博客文章中?,F(xiàn)在,所有你需要知道的是,該標(biāo)準(zhǔn)可以定義一個(gè)全新的符號(hào),比如 Symbol.iterator,同時(shí)它保證不會(huì)與任何現(xiàn)有代碼產(chǎn)生沖突。需要權(quán)衡的是,這樣的語(yǔ)法有點(diǎn)怪異。但對(duì)于其多功能的新特性和出色的向后兼容性,這只是一個(gè)很小的代價(jià)。
一個(gè)具有 [Symbol.iterator]( ) 方法的對(duì)象被稱為可迭代的。在后續(xù)的幾周內(nèi),我們將看到迭代對(duì)象的概念被用于整個(gè)語(yǔ)言,不僅僅用于 for-of 中,也用于 Map 和 Set 構(gòu)造函數(shù),賦值解構(gòu)以及新的傳播算子中。
現(xiàn)在,有一個(gè)機(jī)會(huì),你將永遠(yuǎn)不用從零開始實(shí)現(xiàn)自己的迭代器對(duì)象。我們將在下周解釋原因。但為了完整性,讓我們來看看 iterator 對(duì)象是長(zhǎng)什么樣的。 ( 如果您跳過這一整章節(jié),你可能主要會(huì)缺少一點(diǎn)技術(shù)細(xì)節(jié)。)
一個(gè) for-of 循環(huán)通過調(diào)用集合上的 [Symbol.iterator]( ) 方法進(jìn)行啟動(dòng)。這個(gè)操作將會(huì)返回一個(gè)新的迭代對(duì)象。一個(gè)迭代對(duì)象可以是具有 .next( ) 方法的任意對(duì)象。 然后,for-of 循環(huán)將會(huì)反復(fù)調(diào)用此方法,即每次循環(huán)都要調(diào)用一次。舉例來說,下述應(yīng)該是我能想到的最簡(jiǎn)單的迭代器對(duì)象:
var zeroesForeverIterator = {
[Symbol.iterator]: function () {
return this;
},
next: function () {
return {done: false, value: 0};
}
};
每次 .next() 方法被調(diào)用都會(huì)返回同樣的結(jié)果,以告訴 for-of 循環(huán)以下兩個(gè)信息:( a ) 我們還沒有結(jié)束迭代;( b ) 下一個(gè)值為 0。這就意味著 for ( zeroesForeverIterator 的值 ){}將是一個(gè)無限循環(huán)。當(dāng)然,一個(gè)典型的迭代器不會(huì)這么的微不足道。
這種迭代器的設(shè)計(jì),及其 .done 和 .value 屬性,是與其他語(yǔ)言中的迭代器的工作原理是完全不同的。在 Java 語(yǔ)言中,迭代器有 .hasNext( ) 和 .next( ) 方法。在 Python 語(yǔ)言中, 他們只有一個(gè) single .next( ) 方法以在沒有更多值的時(shí)候拋出停止迭代 ( StopIteration ) 的異常。 但是這三種設(shè)計(jì)本質(zhì)上都返回相同的信息。
一個(gè)迭代器對(duì)象能夠可選擇地實(shí)現(xiàn) .return( ) 和 .throw(exc) 方法。for–of 循環(huán)能通過調(diào)用 .return() 方法來處理過早退出循環(huán)的情況。這種過早退出的情況可能是由于出現(xiàn)異常,中斷或者 return 語(yǔ)句。如果迭代器需要做一些清理工作或者是釋放出其正在使用的一部分資源,其能夠通過實(shí)現(xiàn) .return( ) 以達(dá)到這一目的。絕大多數(shù)的迭代器對(duì)象不需要實(shí)現(xiàn)這一方法。.throw(exc) 可能更是一個(gè)特例: for–of 基本不需要調(diào)用這個(gè)方法。但是我們下周將會(huì)學(xué)習(xí)更多關(guān)于這個(gè)方法的內(nèi)容。
現(xiàn)在,我們已經(jīng)知道了所有的細(xì)節(jié),我們可以采取一個(gè)簡(jiǎn)單的 for-of 循環(huán)并且在底層方法調(diào)用方式進(jìn)行重寫。
首先,for-of 循環(huán):
for (VAR of ITERABLE) {
STATEMENTS
}
這是一個(gè)大致相等的,使用底層方法和一些臨時(shí)變量的例子:
var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
VAR = $result.value;
STATEMENTS
$result = $iterator.next();
}
上述代碼沒有說明怎樣處理 .return( )。我們可以補(bǔ)充一些內(nèi)容,但是我認(rèn)為這樣的話會(huì)掩蓋正在發(fā)生的事情而不是使其變得更加明白易懂。 for-of 是易于使用的,但是很多更深層的東西需要探討。
當(dāng)前所有的 Firefox 發(fā)行版本都支持 for-of 循環(huán)。 要使得其被 Chrome 支持,你需要使用 chrome://flags 并且激活“實(shí)驗(yàn)性的 JavaScript”。其在微軟的瀏覽器斯巴達(dá)也可以運(yùn)行,但不能在 IE 瀏覽器的 shipping 版本中運(yùn)行。如果您想在網(wǎng)絡(luò)上使用這種新語(yǔ)法,你需要支持 IE 和 Safari 瀏覽器,你可以使用一個(gè)像通天塔或谷歌的 Traceur 的編譯器來將你的 ES6 的代碼轉(zhuǎn)換為 Web 友好的 ES5 的代碼。
在服務(wù)器上,你并不需要一個(gè)編譯器,你今天就可以在 io.js ( 或者 Node 以 --harmony 選項(xiàng)啟動(dòng) ) 上開始使用 for-of。
( 更新:以前被忽視但仍需要一提的是,for-of 在默認(rèn)情況下是被 Chrome 瀏覽器禁用的。 )
好了,本章結(jié)束了,但是我們?nèi)匀徊荒芡耆莆?for-of 循環(huán)。
在 ES6 中,有新一類的對(duì)象能夠與 for-of 完美地配合使用。我覺得這個(gè)新功能是 ES6 中最神奇的事情。如果你還沒有在 Python 和 C#語(yǔ)言中遇到,你可能最初覺得它是多么令人難以置信。但它是寫迭代器的最簡(jiǎn)單的方法,它在重構(gòu)時(shí)是很有用的,它可能改變了我們無論在瀏覽器還是在服務(wù)器上編寫異步代碼的方式。因此,讓我們看看 ES6 生成器并進(jìn)行深入探討。