【注】其實吸引個人是這張配圖。。。讓我想起了卷福劃的圈,有沒有~~javascript
咱們將在文章中分析一下Iterators(迭代器)。Iterators是JS中循環集合的一種新的方式。它是ES6中引入的,已經在不少場景中被普遍使用,所以已經變得很是受歡迎。html
若是你有這樣一個數組:java
const myFavouriteAuthors = [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
];
複製代碼
有時候,你想要獲取數組裏的每一個值,用來打印,操做,或者作一些其它邏輯。若是我問你怎麼作?你確定會說 —— 這很簡單。「用for
,while
,for-of
就好了」。實現將會是這樣:es6
設想一下,若是你須要自定義一下剛剛那個數組結構,好比保存做者信息,像這樣:數組
如今,myFavouriteAuthors
是一個對象了,包含了另外一個對象allAuthors
。allAuthors
又包含了3個數組,fiction
、scienceFiction
以及fantasy
。如今,若是我再問你如何經過循環myFavouriteAuthors
來獲取全部做者,那你會怎麼來實現呢?你能夠繼續嘗試用複合循環來獲取全部數據。數據結構
不過,若是你這樣作:app
for (let author of myFavouriteAuthors) {
console.log(author)
}
// TypeError: {} is not iterable
複製代碼
你將會獲得TypeError
說object不能被迭代。讓咱們看下可迭代是什麼意思,而且咱們如何才能讓這個object能夠迭代。在文章最後,你將會知道如何用for-of
去迭代這個myFavouriteAuthors
。ide
**Iterables(迭代) and Iterators(迭代器)函數
上一節咱們遇到了個問題。在咱們自定義的對象裏獲取全部做者信息不是那麼的方便。咱們須要某種方法,可以把全部(對象)內數據有順序的暴露出來。ui
讓咱們在myFavouriteAuthors
里加一個方法getAllAuthors
,用來返回全部做者,像這樣:
這是個簡單的實現。它完成了咱們的任務。然而,這樣會出現一些問題:
getAllAuthors
這個命名太自由了。若是有些人有一個他們本身的myFavouriteAuthors
,他們可能會取名叫retrieveAllAuthors
。getAllAuthors
。getAllAuthors
返回的是包含全部做者字符串的數組。但若是另一個開發人員返回了一個對象組成的數組,怎麼辦呢?[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]
那麼開發人員必需要知道這個方法的準確名字以及返回的數據格式。
若是咱們定一個規則,方法的名字和返回類型是固定不可變的呢?
讓咱們把這個方法叫作 —— iteratorMethod
ECMA採起了相似的步驟來標準化這個循環自定義對象的過程。然而,ECMA用名爲Symbol.iterator
來代替了iteratorMethod
名字。Symbols提供了一個獨一無二的名字(惟一,而且不會和別的屬性名發生衝突)。Symbol.iterator
將會返回一個叫作iterator
的對象。這個iterator有一個next
的方法,這個方法會返回一個用value
和done
做爲key的對象。
這個叫value
的key將包含當前值。它是任何類型的。這個done
是一個布爾值。它表示是否已經獲取到了全部的值。
用一張圖來幫助說明一下 iterables, iterators , next 之間的關係。而 這個關係被稱爲 Iteration Protocol(迭代協議) 。
按照Axel Rauschmayer的Exploring JS這本書所說:
Symbol.iterator
就是用來作這件事的方法。這個方法是一個 迭代器 工廠,也就是說,它將會建立一個 迭代器 。** 讓 objects
變得可迭代
咱們已經從上一節瞭解到,咱們須要實現一個叫作 Symbol.iterator
的方法。咱們將會使用computed property syntax(可計算的屬性語法)來設置這個key。示例以下:
第四行,咱們定義了一個迭代器。是一個帶有next
方法的對象。這個next
方法返回的值取決於step
變量。在25行,咱們獲取了這個iterator
。27行,咱們調用next
。咱們調用next屢次,直到done
變成true
。
這正是在for-of
循環裏發生的事情。for-of
拿到這個對象的迭代器,而後循環調用next()
直到done
變成true。
JS裏有不少可迭代的東西。可能不是顯而易見的,可是你仔細檢查一下,迭代器就會出來了。
arguments
—— 相似arrayfor-of
循環 —— 須要對象可迭代,不然會拋出一個TypeError
。for (const value of iterable) { ... }
const array = ['a', 'b', 'c', 'd', 'e'];
const [first, ,third, ,last] = array;
複製代碼
這樣就等同於:
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value
複製代碼
代碼以下:
const array = ['a', 'b', 'c', 'd', 'e'];
const newArray = [1, ...array, 2, 3];
複製代碼
等同於:
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next(); nextValue.done !== true; nextValue = iterator.next()) {
newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)
複製代碼
Map的構造函數會迭代出[key,value]
放進Map裏,Set的構造函數會迭代出元素放進Set裏:
const map = new Map([[1, 'one'], [2, 'two']]);
map.get(1)
// one
const set = new Set(['a', 'b', 'c]);
set.has('c');
// true
複製代碼
myFavouriteAuthors
能夠迭代直接上代碼:
const myFavouriteAuthors = {
allAuthors: {
fiction: [
'Agatha Christie',
'J. K. Rowling',
'Dr. Seuss'
],
scienceFiction: [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
],
fantasy: [
'J. R. R. Tolkien',
'J. K. Rowling',
'Terry Pratchett'
],
},
[Symbol.iterator]() {
// Get all the genre in an array
const genres = Object.values(this.allAuthors);
// Store the current genre and author index
let currentAuthorIndex = 0;
let currentGenreIndex = 0;
return {
// Implementation of next()
next() {
// authors according to current genre index
const authors = genres[currentGenreIndex];
// doNotHaveMoreAuthors is true when the authors array is exhausted.
// That is, all items are consumed.
const doNothaveMoreAuthors = !(currentAuthorIndex < authors.length);
if (doNothaveMoreAuthors) {
// When that happens, we move the genre index to the next genre
currentGenreIndex++;
// and reset the author index to 0 again to get new set of authors
currentAuthorIndex = 0;
}
// if all genres are over, then we need tell the iterator that we
// can not give more values.
const doNotHaveMoreGenres = !(currentGenreIndex < genres.length);
if (doNotHaveMoreGenres) {
// Hence, we return done as true.
return {
value: undefined,
done: true
};
}
// if everything is correct, return the author from the
// current genre and incerement the currentAuthorindex
// so next time, the next author can be returned.
return {
value: genres[currentGenreIndex][currentAuthorIndex++],
done: false
}
}
};
複製代碼
引用:
ExploringJS by Dr Axel Rauschmayer.