1:Iterator(遍歷器)概念
集合數(shù)據(jù)結(jié)構(gòu):Array,Object,Map,Set
用戶可以組合使用冈涧,定義自己的數(shù)據(jù)結(jié)構(gòu)。比如:數(shù)組中有Map,Map中有對象遇伞。這樣就需要一種統(tǒng)一的接口機制晾剖。遍歷器就是這樣一種機制温亲,來處理所有不同的數(shù)據(jù)結(jié)構(gòu)。
遍歷器是一種接口匕得。
遍歷器為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制继榆。任何數(shù)據(jù)結(jié)構(gòu)只要部署Iterator接口巾表,就可以完成遍歷操作: for ...of循環(huán)
作用:
1:為各種數(shù)據(jù)結(jié)構(gòu),提供統(tǒng)一的略吨,簡便的訪問接口
2:使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列
3:ES6創(chuàng)造了一種新的遍歷命令for...of循環(huán)集币,Iterator接口主要提供for...of消費
遍歷過程:
1:創(chuàng)建一個指針對象,指向當前數(shù)據(jù)結(jié)構(gòu)的起始位置翠忠。遍歷器對象本質(zhì)上是一個指針對象鞠苟。{next:function(){} }
2:第一次調(diào)用指針對象的next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員秽之。
3:第二次調(diào)用指針對象的next方法当娱,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個成員。
4:不斷調(diào)用指針對象的next方法考榨,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置跨细。
每一次調(diào)用next方法,都會返回數(shù)據(jù)結(jié)構(gòu)的當前成員的信息河质。具體來說冀惭,就是返回一個包含value和done兩個屬性的對象。其中value是當前成員的值掀鹅,done是一個布爾值云头,表示遍歷是否結(jié)束。
{value:'someValue',done:false}
指針對象的next方法淫半,用來移動指針溃槐。開始時指針指向數(shù)組的開始位置,然后每次調(diào)用next方法科吭,指針就會指向數(shù)組的下一個成員:
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} : //i++來移動指針
{value: undefined, done: true}; //最后以為done為true
}
};}
Iterator只是把接口規(guī)格加到數(shù)據(jù)結(jié)構(gòu)之上昏滴,所以,遍歷器與它所遍歷的那個數(shù)據(jù)結(jié)構(gòu)对人,實際上是分開的谣殊,完全可以寫出沒有對應(yīng)數(shù)據(jù)結(jié)構(gòu)的遍歷器對象,或者說用遍歷器對象模擬出數(shù)據(jù)結(jié)構(gòu)牺弄。
for...of循環(huán)會自動尋找Iterator接口
一種數(shù)據(jù)結(jié)構(gòu)只要部署了 Iterator 接口姻几,我們就稱這種數(shù)據(jù)結(jié)構(gòu)是“可遍歷的”(iterable)。
ES6 規(guī)定势告,默認的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性蛇捌,或者說,一個數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性咱台,就可以認為是“可遍歷的”(iterable)
interface Iterable {
[Symbol.iterator]() : Iterator, // 部署了iterator接口络拌,Iterator是一個函數(shù),就是當前數(shù)據(jù)結(jié)構(gòu)默認的遍歷器生成函數(shù)回溺。
}
遍歷器接口其實就是一個方法春贸,方法返回一個指針對象混萝,指針對像中有next方法,next方法返回一個含有value,done屬性的對象
const obj = {
[Symbol.iterator]:function(){
return { //指針對象
next:function(){
return {
value:i++,
done:false
}
}
}
}
}
凡是部署了Symbol.iterator屬性的數(shù)據(jù)結(jié)構(gòu)萍恕,就稱為部署了遍歷器接口逸嘀。調(diào)用這個接口,就會返回一個遍歷器對象允粤。
原生具有Iterator接口的數(shù)據(jù)結(jié)構(gòu)如下:Array,Map,Set厘熟,String,TypedArray,arguments對象,NodeList對象
對于原生部署 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)维哈,不用自己寫遍歷器生成函數(shù)绳姨,for...of循環(huán)會自動遍歷它們。除此之外阔挠,其他數(shù)據(jù)結(jié)構(gòu)(主要是對象)的 Iterator 接口飘庄,都需要自己在Symbol.iterator屬性上面部署,這樣才會被for...of循環(huán)遍歷购撼。
一個對象如果要具備可被for...of循環(huán)調(diào)用的 Iterator 接口跪削,就必須在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的對象具有該方法也可)。
如果Symbol.iterator方法對應(yīng)的不是遍歷器生成函數(shù)(即會返回一個遍歷器對象)迂求,解釋引擎將會報錯碾盐。
2:調(diào)用Iterator接口的場合:
2.1:解構(gòu)賦值
對數(shù)組和Set結(jié)構(gòu)進行解構(gòu)賦值時,會默認調(diào)用Symbol.iterator方法
2.2:擴展運算符
只要某個數(shù)據(jù)結(jié)構(gòu)部署了 Iterator 接口揩局,就可以對它使用擴展運算符毫玖,將其轉(zhuǎn)為數(shù)組。
2.3:yield*
yield*后面跟的是一個可遍歷的結(jié)構(gòu)凌盯,它會調(diào)用該結(jié)構(gòu)的遍歷器接口付枫。
2.4:其他
由于數(shù)組的遍歷會調(diào)用遍歷器接口,所以任何接受數(shù)組作為參數(shù)的場合驰怎,其實都調(diào)用了遍歷器接口阐滩。
* for...of
* Array.from()
* Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
* Promise.all()
* Promise.race()
3:字符串也有Iterator接口
可以覆蓋原生的Symbol.iterator方法,達到修改遍歷器行為的目的县忌。(可以偷偷改掉遍歷輸出的內(nèi)容)
對于字符串來說掂榔,for...of循環(huán)還有一個特點,就是會正確識別 32 位 UTF-16 字符症杏。
4:Iterator接口和Generator函數(shù)
Symbol.iterator()方法幾乎不用部署任何代碼装获,只要用 yield 命令給出每一步的返回值即可
5:遍歷器對象的return,throw方法
遍歷器對象除了具有next方法,還可以具有return和throw方法鸳慈。 如果你自己寫遍歷器對象生成函數(shù)饱溢,那么next()方法是必須部署的喧伞,return()方法和throw()方法是否部署是可選的走芋。
return()方法的使用場合是:
4.1:如果for...of循環(huán)提前退出(通常是因為出錯绩郎,或者有break語句),就會調(diào)用return()方法翁逞。
4.2:如果一個對象在完成遍歷前肋杖,需要清理或釋放資源,就可以部署return()方法挖函。
return()方法必須返回一個對象状植,這是 Generator 語法決定的。
throw()方法的使用場合是:
主要是配合 Generator 函數(shù)使用怨喘,一般的遍歷器對象用不到這個方法津畸。請參閱《Generator 函數(shù)》一章
6:for...of循環(huán)
引入了for...of循環(huán),作為遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一的方法必怜。
let arr = new Array(5).fill({a:1})
for(i of arr){console.log(i)} // 獲得鍵值
for(i in arr){console.log(i)} //獲取鍵名
for...in循環(huán)讀取鍵名肉拓,for...of循環(huán)讀取鍵值。如果要通過for...of循環(huán)梳庆,獲取數(shù)組的索引暖途,可以借助數(shù)組實例的entries方法和keys方法(參見《數(shù)組的擴展》一章)。
7: Set 和 Map 結(jié)構(gòu)也原生具有 Iterator 接口膏执,可以直接使用for...of循環(huán)驻售。
值得注意的地方有兩個,首先更米,遍歷的順序是按照各個成員被添加進數(shù)據(jù)結(jié)構(gòu)的順序欺栗。其次,Set 結(jié)構(gòu)遍歷時征峦,返回的是一個值纸巷,而 Map 結(jié)構(gòu)遍歷時,返回的是一個數(shù)組眶痰,該數(shù)組的兩個成員分別為當前 Map 成員的鍵名和鍵值瘤旨。
并不是所有類似數(shù)組的對象都具有 Iterator 接口,一個簡便的解決方法竖伯,就是使用Array.from方法將其轉(zhuǎn)為數(shù)組存哲。
8:對象:
對于普通的對象,for...of結(jié)構(gòu)不能直接使用七婴,會報錯祟偷,必須部署了 Iterator 接口后才能使用。但是打厘,這樣情況下修肠,for...in循環(huán)依然可以用來遍歷鍵名。
一種解決方法是户盯,使用Object.keys方法將對象的鍵名生成一個數(shù)組嵌施,然后遍歷這個數(shù)組饲化。
另一個方法是使用 Generator 函數(shù)將對象重新包裝一下。
9:與其他遍歷語法的比較
for : 比較麻煩
forEach: 無法中途跳出forEach循環(huán)吗伤,break命令或return命令都不能奏效
for...in 循環(huán)可以遍歷數(shù)組的鍵名 缺點:
* 數(shù)組的鍵名是數(shù)字吃靠,但是for...in循環(huán)是以字符串作為鍵名“0”、“1”足淆、“2”等等巢块。
* for...in循環(huán)不僅遍歷數(shù)字鍵名,還會遍歷手動添加的其他鍵巧号,甚至包括原型鏈上的鍵族奢。
* 某些情況下,for...in循環(huán)會以任意順序遍歷鍵名丹鸿。
總之: for...in循環(huán)主要是為遍歷對象而設(shè)計的歹鱼,不適用于遍歷數(shù)組。
for...of:優(yōu)點:
* 有著同for...in一樣的簡潔語法卜高,但是沒有for...in那些缺點弥姻。
* 不同于forEach方法,它可以與break掺涛、continue和return配合使用庭敦。
* 提供了遍歷所有數(shù)據(jù)結(jié)構(gòu)的統(tǒng)一操作接口。
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}