參考:ECMAScript 6 入門
簡單介紹:
我的理解:所謂可遍歷杭朱,就是可以取到下一個(gè)纽疟。重點(diǎn)在于實(shí)現(xiàn)一個(gè)可以取到下一個(gè)對象的next方法
首先巧颈,Iterator適合于容器對象辜纲,既可以存儲其它對象的對象,比如:Array, Map, Set等绞绒。
其內(nèi)部通過next方法可以不斷獲取存儲的里面的下一個(gè)對象婶希,實(shí)現(xiàn)了這樣的next方法的對象便是可遍歷的。
嚴(yán)謹(jǐn)一點(diǎn)蓬衡,一種數(shù)據(jù)結(jié)構(gòu)只要部署了 Iterator 接口喻杈,我們就稱這種數(shù)據(jù)結(jié)構(gòu)是“可遍歷的”(iterable)。
Iterator接口應(yīng)該長什么樣兒的狰晚?使用TypeScript的寫法如下:
interface Iterable {
[Symbol.iterator]() : Iterator,
}
interface Iterator {
// next方法是重點(diǎn)筒饰,通過next方法來獲取下一個(gè)對象
next(value?: any) : IterationResult,
}
interface IterationResult {
value: any,
done: boolean,
}
ES6 創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費(fèi)家肯。當(dāng)使用for...of循環(huán)遍歷某種數(shù)據(jù)結(jié)構(gòu)時(shí)龄砰,該循環(huán)會自動去尋找 Iterator 接口盟猖。一個(gè)數(shù)據(jù)結(jié)構(gòu)只要部署了Symbol.iterator屬性讨衣,就被視為具有 iterator 接口换棚,就可以用for...of循環(huán)遍歷它的成員。也就是說反镇,for...of循環(huán)內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator方法固蚤。
原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)如下
Array
Map
Set
String
TypedArray
函數(shù)的 arguments 對象
NodeList 對象
這些數(shù)據(jù)結(jié)構(gòu)原生部署了Symbol.iterator
屬性
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator](); //調(diào)用Array對象的Symbol.iterato屬性方法生成遍歷器
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
除了for...of,還有哪些是使用iterator的變種(請注意歹茶,Object對象默認(rèn)沒有部署 Iterator 接口)
- 解構(gòu)賦值:對數(shù)組和 Set 結(jié)構(gòu)進(jìn)行解構(gòu)賦值時(shí)夕玩,會默認(rèn)調(diào)用Symbol.iterator方法。
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
- 擴(kuò)展運(yùn)算符:擴(kuò)展運(yùn)算符(...)也會調(diào)用默認(rèn)的 Iterator 接口惊豺。
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
- yield:yield后面跟的是一個(gè)可遍歷的結(jié)構(gòu)燎孟,它會調(diào)用該結(jié)構(gòu)的遍歷器接口。
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
- 字符串對象也實(shí)現(xiàn)了 Iterator 接口(我的理解尸昧,字符串本質(zhì)上就是一個(gè)char數(shù)組揩页,所以它可以被遍歷也是情理之中的了)
var someString = "hi";
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
使用 Generator 函數(shù)實(shí)現(xiàn)遍歷接口
let myIterable = {
[Symbol.iterator]: function* () {
yield 1; // yield 是產(chǎn)出的意思
yield 2;
yield 3;
}
}
[...myIterable] // [1, 2, 3]
// 或者采用下面的簡潔寫法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
}
// "hello"
// "world"
其它遍歷器對象:
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ['a', 1]
// ['b', 2]
像上面的例子,map應(yīng)該調(diào)用了它默認(rèn)的遍歷生成器烹俗。如果我只想遍歷map的key或者value怎么辦呢爆侣?
答案是通過:entries(), keys(), values()
等。
ES6 的數(shù)組幢妄、Set兔仰、Map 都部署了以下三個(gè)方法,調(diào)用后都返回遍歷器對象蕉鸳。
-
entries()
返回一個(gè)遍歷器對象乎赴,用來遍歷并返回[鍵名, 鍵值]組成的數(shù)組。對于數(shù)組潮尝,鍵名就是索引值无虚;對于 Set,鍵名與鍵值相同衍锚。Map 結(jié)構(gòu)的 Iterator 接口友题,默認(rèn)就是調(diào)用entries方法。 -
keys()
返回一個(gè)遍歷器對象戴质,用來遍歷所有的鍵名度宦。 -
values()
返回一個(gè)遍歷器對象,用來遍歷所有的鍵值告匠。
類數(shù)組對象舉例:
let arrayLike = { length: 2, 0: 'a', 1: 'b' };//類數(shù)組對象
// 報(bào)錯
for (let x of arrayLike) {
console.log(x);
}
// 正確戈抄,先使用Array.from將其轉(zhuǎn)為帶 Iterator 接口的數(shù)組
for (let x of Array.from(arrayLike)) {
console.log(x);
}
無法中途跳出forEach循環(huán),break命令或return命令都不能奏效后专。
非重點(diǎn):
遍歷器對象的 return()划鸽,throw()
遍歷器對象除了具有next方法,還可以具有return方法和throw方法。如果你自己寫遍歷器對象生成函數(shù)裸诽,那么next方法是必須部署的嫂用,return方法和throw方法是否部署是可選的。
return方法的使用場合是丈冬,如果for...of循環(huán)提前退出(通常是因?yàn)槌鲥e嘱函,或者有break語句),就會調(diào)用return方法埂蕊。如果一個(gè)對象在完成遍歷前往弓,需要清理或釋放資源,就可以部署return方法蓄氧。
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
}
上面代碼中函似,函數(shù)readLinesSync接受一個(gè)文件對象作為參數(shù),返回一個(gè)遍歷器對象喉童,其中除了next方法缴淋,還部署了return方法。下面的兩種情況泄朴,都會觸發(fā)執(zhí)行return方法重抖。
// 情況一
for (let line of readLinesSync(fileName)) {
console.log(line);
break;
}
// 情況二
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error();
}
上面代碼中,情況一輸出文件的第一行以后祖灰,就會執(zhí)行return方法钟沛,關(guān)閉這個(gè)文件;情況二會在執(zhí)行return方法關(guān)閉文件之后局扶,再拋出錯誤恨统。
注意,return方法必須返回一個(gè)對象三妈,這是 Generator 規(guī)格決定的畜埋。
throw方法主要是配合 Generator 函數(shù)使用,一般的遍歷器對象用不到這個(gè)方法畴蒲。