
ECMAScript 6 規(guī)范新增的迭代器是一種新的遍歷機制,能夠更清晰、高效、方便地實現(xiàn)迭代。所有通過迭代器訪問的結構都實現(xiàn)了 Iterator 接口,且能夠在 for...of 循環(huán)中應用。
可迭代協(xié)議
可迭代協(xié)議指的是實現(xiàn) Iterable 接口并同時支持迭代的自我識別能力和創(chuàng)建實現(xiàn) Iterator 接口的對象的能力。任何實現(xiàn) Iterable 接口的對象都有一個 Symbol.iterator 屬性,這個屬性引用默認迭代器。默認迭代器是一個函數(shù),調(diào)用之后會產(chǎn)生一個實現(xiàn) Iterator 接口的對象。內(nèi)置的可迭代對象有 String、Array、Map 和 Set 等等。
下面給出幾個例子,可以檢查是否存在默認迭代器屬性:
let obj = {};
let num = 3;
// Object 和 Number 都沒有實現(xiàn)迭代器函數(shù)
console.log(obj[Symbol.iterator]); // undefined
console.log(num[Symbol.iterator]); // undefined
let arr = ["小明", "小李"];
let set = new Set().add('小明').add('小李');
// Array 和 Set 都實現(xiàn)了迭代器函數(shù)
console.log(arr[Symbol.iterator]); // [Function: values]
console.log(set[Symbol.iterator]); // [Function: values]
// 下面獲取迭代器
console.log(arr[Symbol.iterator]()); // Object [Array Iterator] {}
console.log(set[Symbol.iterator]()); // [Set Iterator] { '小明', '小李' }
實際上,實現(xiàn)可迭代協(xié)議的迭代對象都可以在如下場景使用:
-
for-of循環(huán) - 數(shù)組解構
- 擴展操作符
Array.from()- 集合與映射
- 由
Promise組成的可迭代對象 -
yield*操作符
let arr = ["小玲", "小霞", "小星", "小民"];
// for-of循環(huán)
for (let v of arr) {
console.log(v);
}
// 數(shù)組解構
let [a1, a2, a3, a4] = arr;
console.log(a1, a2, a3, a4); // 小玲 小霞 小星 小民
// 擴展操作符
let arr2 = [...arr];
console.log(arr2); // [ '小玲', '小霞', '小星', '小民' ]
// Array.from()
let arr3 = Array.from(arr);
console.log(arr3); // [ '小玲', '小霞', '小星', '小民' ]
如果對象原型鏈上的父類實現(xiàn)了 Iterable 接口,那這個對象也會實現(xiàn) Iterable 接口:
如果對象原型鏈上的父類實現(xiàn)了 Iterable 接口,那這個對象也就實現(xiàn)了這個接口:
class List extends Array {}
let list = new List("小玲", "小霞", "小星", "小民");
for (let v of list1) {
console.log(v);
}
// 小玲
// 小霞
// 小星
// 小民
迭代器協(xié)議
迭代器協(xié)議定義了產(chǎn)生一些列值的標準方法。而對象只有實現(xiàn)了 next() 方法才能成為迭代器。對象使用 next() 遍歷數(shù)據(jù)時,每次成功都會返回一個 IteratorResult 對象,其中包含迭代返回的下一個值;所有的值都迭代完后,會返回一個默認返回值;若不調(diào)用 next(),則無法知道迭代器的當前位置。
next() 方法返回的 IteratorResult 包含兩個屬性:
-
done:布爾值。表示是否還可以調(diào)用next()獲取下一個值。 迭代器遍歷完畢會返回true;否則返回false。 -
value:迭代器返回的值。done為true可省略或undefined。
可以通過以下簡單的數(shù)組來演示:
let arr = ["小明", "小李"];
// 獲取迭代器
let iter = arr[Symbol.iterator]();
// 執(zhí)行迭代
console.log(iter.next()); // { value: '小明', done: false }
console.log(iter.next()); // { value: '小李', done: false }
console.log(iter.next()); // { value: undefined, done: true }
console.log(iter.next()); // { value: undefined, done: true }
在迭代期間修改了迭代對象,會反映在迭代對象上,如下所示:
let arr = ["小明", "小李"];
// 獲取迭代器
let iter = arr[Symbol.iterator]();
console.log(iter.next()); // { value: '小明', done: false }
console.log(iter.next()); // { value: '小李', done: false }
arr.splice(2, 0, "小虎");
console.log(iter.next()); // { value: '小虎', done: false }
console.log(iter.next()); // { value: undefined, done: true }
console.log(iter.next()); // { value: undefined, done: true }
這是因為使用 next() 獲取對象只是使用游標來記錄歷程。
<small>注意:迭代器維護著一個指向可迭代對象的引用,因此迭代器會阻止垃圾回收程序回收可迭代對象。</small>
自定義迭代器
因為每個迭代器也實現(xiàn)了Iterable 接口,所以它們可以用在任何期待可迭代對象的地方,比如for-of 循環(huán):
每個實現(xiàn)了 Iterable 接口的迭代器都可以用在任何可迭代對象的地方,如 for-of 循環(huán)。下面自定義一個實現(xiàn)迭代器的類:
class List {
constructor() {
this.index = 0;
this.data = arguments;
}
[Symbol.iterator]() {
return {
next: () => {
if (this.index < this.data.length) {
return { value: this.data[this.index++], done: false};
}
this.index = 0;
return {done: true, value: undefined};
}
}
}
}
let list = new List("小玲", "小霞", "小星", "小民");
let iter = list[Symbol.iterator]();
console.log(iter.next()); // { value: '小玲', done: false }
console.log(iter.next()); // { value: '小霞', done: false }
console.log(iter.next()); // { value: '小星', done: false }
console.log(iter.next()); // { value: '小民', done: false }
console.log(iter.next()); // { done: true, value: undefined }
任何實現(xiàn) Iterable 接口的數(shù)據(jù)結構都可以實現(xiàn) Iterator 接口的結構 “消費”。
提前終止迭代器
當使用 for-of 循環(huán)迭代器時,如果不想迭代下去,可以通過 break、continue、return 或 throw 提前退出。
for (let v of list) {
if (v === '小星') {
console.log("提前退出");
break;
}
console.log(v);
}
// 小玲
// 小霞
// 提前退出
總結
迭代器是 ES6 新增的重要功能,提供了一個可以由任意對象實現(xiàn)的接口,并連續(xù)返回迭代對象中的每個值。任何實現(xiàn) Iterable 接口的對象都有一個 Symbol.iterator 屬性,這個屬性引用默認迭代器。當一個對象中存在 Symbol.iterator 時,該對象就會被認為是可迭代對象。
更多內(nèi)容請關注公眾號「海人為記」