Iterator 迭代器
迭代器(Iterator)接口社裆,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機(jī)制拙绊。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口泳秀,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)标沪。
1. Iterator 作用
- 為各種數(shù)據(jù)結(jié)構(gòu)嗜傅,提供一個統(tǒng)一的金句、簡便地訪問接口吕嘀;
- 使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列违寞;
- ES6 創(chuàng)造了一種新的遍歷命令 for...of 循環(huán)趁曼,Iterator 接口主要供 for...of 消費(fèi)。
2. Iterator 遍歷
1)創(chuàng)建一個指針對象挡闰,指向當(dāng)前數(shù)據(jù)結(jié)構(gòu)的起始位置;
2)第一次調(diào)用指針對象的 next 方法,將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員(包含 value 和 done 兩個屬性的對象);
3)第二次調(diào)用指針對象的 next 方法掰盘,將指針指向數(shù)據(jù)結(jié)構(gòu)的第二個成員;
4)不斷調(diào)用指針對象的 next 方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置愧捕。
3. Iterator 接口
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
type IteratorResult<T, TReturn = any> =
| IteratorYieldResult<T>
| IteratorReturnResult<TReturn>;
interface Iterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
interface IterableIterator<T> extends Iterator<T> {
[Symbol.iterator](): IterableIterator<T>;
}
4. Iterator 接口實(shí)現(xiàn)
function makeIterator(array) {
var nextIndex = 0;
return {
next: function () {
return nextIndex < array.length
? { value: array[nextIndex++], done: false }
: { value: undefined, done: true };
},
};
}
const it = makeIterator(["a", "b"]);
it.next(); // { value: "a", done: false }
it.next(); // { value: "b", done: false }
it.next(); // { value: undefined, done: true }
5. 默認(rèn)的 Iterator 接口
ES6 規(guī)定瘪阁,默認(rèn)的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的 Symbol.iterator
屬性罗洗,或者說,一個數(shù)據(jù)結(jié)構(gòu)只要具有 Symbol.iterator 屬性轩缤,就可以認(rèn)為是“可遍歷的”(iterable)火的。
Symbol.iterator
屬性本身是一個函數(shù)馏鹤,就是當(dāng)前數(shù)據(jù)結(jié)構(gòu)默認(rèn)的遍歷器生成函數(shù)娇哆。執(zhí)行這個函數(shù)碍讨,就會返回一個遍歷器治力。
Symbol.iterator
屬性名是一個表達(dá)式勃黍,返回 Symbol 對象的 iterator 屬性宵统,是一個預(yù)定義好的覆获、類型為 Symbol 的特殊值马澈。
如下弄息,將對象通過 Symbol.iterator
變成一個可遍歷的對象:
const obj = {
[Symbol.iterator]: function () {
return {
next: function () {
return {
value: 1,
done: true,
};
},
};
},
};
6. 原生可迭代對象
原生具備 Iterator 接口的數(shù)據(jù)結(jié)構(gòu)如下:
- Array
- Map
- Set
- String
- TypedArray
- 函數(shù)的 arguments 對象
- NodeList 對象
如下痊班,獲取數(shù)組的遍歷器對象:
const arr = ["a", "b", "c"];
const iter = arr[Symbol.iterator]();
iter.next(); // { value: 'a', done: false }
iter.next(); // { value: 'b', done: false }
iter.next(); // { value: 'c', done: false }
iter.next(); // { value: undefined, done: true }
關(guān)于對象(Object)未部署 Iterator 接口的原因:
對象的屬性遍歷順序是無法確定的疑枯,需要開發(fā)者手動指定辩块。遍歷器本質(zhì)上是一種線性處理荆永,對于非線性的數(shù)據(jù)接口部署遍歷器接口废亭,就等于部署一種線性轉(zhuǎn)換具钥。另外對象部署遍歷器接口并不是必要的,因?yàn)槿绻渴鹆司偷扔诒划?dāng)作 Map 結(jié)構(gòu)使用骂删,而 ES6 原生提供 Map 的掌动。
7. 遍歷器對象的 return(),throw()
遍歷器對象除了具有 next()方法粗恢,還可以具有 return()方法和 throw()方法。如果你自己寫遍歷器對象生成函數(shù)眷射,那么 next()方法是必須部署的匙赞,return()方法和 throw()方法是否部署是可選的妖碉。
return()方法的使用場合是涌庭,如果 for...of 循環(huán)提前退出(通常是因?yàn)槌鲥e欧宜,或者有 break 語句),就會調(diào)用 return()方法冗茸。
如果一個對象在完成遍歷前席镀,需要清理或釋放資源蚀狰,就可以部署 return()方法愉昆。
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
},
};
},
};
}
8. 類數(shù)組對象
字符串麻蹋、DOM NodeList 對象、arguments 對象都實(shí)現(xiàn)了遍歷器接口扮授,所以可用 for...of
進(jìn)行遍歷:
// 字符串
let str = "hello";
for (let s of str) {
console.log(s); // h e l l o
}
// DOM NodeList對象
let paras = document.querySelectorAll("p");
for (let p of paras) {
p.classList.add("test");
}
// arguments對象
function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs("a", "b");
// 'a'
// 'b'
并不是所有類似數(shù)組的對象都具有 Iterator 接口,一個簡便的解決方法刹勃,就是使用 Array.from
方法將其轉(zhuǎn)為數(shù)組。
const arrayLike = { length: 2, 0: "a", 1: "b" };
// 報(bào)錯
for (let x of arrayLike) {
console.log(x);
}
// 正確
for (let x of Array.from(arrayLike)) {
console.log(x);
}