ES6 中的 iterator
【簡介】
遍歷器/叠代器。任何數據結構只要部署 Iterator 接口,就可以完成遍歷操作。這種數據結構是“可遍歷的”(iterable)。
如何判斷是否可遍歷?
typeof target[Symbol.iterator] // function
【作用】
1. 為各種數據結構,提供一個統一的、簡便的訪問接口;
2. 使得數據結構的成員能夠按某種次序排列;
3. ES6 創造了一種新的遍歷命令for...of 循環,Iterator 接口主要供for...of消費。
【遍歷】
const colors = ["red", "green", "blue"]for (var i = 0, len = colors.length; i < len; i++) { console.log(colors[i]) }
- 追蹤下標位置,
- 判斷循環何時停止。
【自定義 iterator】
function createIterator(items) { var i = 0 return { next: function () { var done = (i >= items.length) var value = !done ? items[i++] : undefinedreturn { done: done, value: value, } } } } var iterator = createIterator([1, 2, 3]) console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next())
Iterator 的遍歷過程:
1、創建一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。
2、第一次調用指針對象的next方法,可以將指針指向數據結構的第一個成員。
3、不斷調用指針對象的next方法,直到它指向數據結構的結束位置。
雖然是比 for 循環簡單了些,但手動寫個 iterator 太麻煩了,所以ES6 推出 generator ,方便創建 iterator。也就是說,generator 就是一個返回值為 iterator 的函數。
【generator 和 iterator】
function* createIterator() { yield 1 yield 2 yield 3 } let iterator = createIterator() console.log(iterator.next().value) console.log(iterator.next().value) console.log(iterator.next().value)
Generator:ES6 提供的一種異步編程解決方案。
執行 Generator 函數會返回一個 iterator 對象,通過這個對象可以依次遍歷 Generator 函數內部的每一個狀態。
【for…of 和 iterator】
const colors = ["red", "green", "blue"]; for (let color of colors) { console.log(color); }
當使用for…of循環遍歷某種數據結構時,該循環會自動去尋找 Iterator 接口,並調用Symbol.iterator方法,返回該對象的默認遍歷器。
for…of循環可以使用的範圍包括數組、Set 和 Map、類似數組對象(arguments、DOM NodeList、 Generator、字符串)。
【內置的 iterator】
ES6 的有些數據結構原生具備 Iterator 接口(比如數組),即不用任何處理,就可以被 for…of 循環遍歷。
原因在於,這些數據結構原生部署了Symbol.iterator屬性,另外一些數據結構沒有(比如對象)。
凡是部署了Symbol.iterator屬性的數據結構,就稱為部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。
原生具備 Iterator 接口的數據結構如下。
Array、Map、Set、String、arguments 、NodeList
// Array iterator const heros = [‘Tony‘, ‘Steve‘, ‘Natasha‘, ‘Banner‘, ‘Thor‘] for (const hero of heros) { console.log(hero) } // Map iterator const sex = ‘male‘ const userMap = new Map([ [‘name‘, ‘Stark‘], [48, ‘age‘ ], [‘gender‘, sex] ]) for (let entry of userMap.entries()) { console.log(entry) } for (let key of userMap.keys()) { console.log(key) } for (let value of userMap.values()) { console.log(value) } const numberSet = new Set([1, 2, 2, 3, 4, 4, 5]) for (let entry of numberSet.entries()) { console.log(entry) } for (let key of numberSet.keys()) { console.log(key) } for (let value of numberSet.values()) { console.log(value) } // String iterator const str = ‘ca ?? r‘for (const c of str) { console.log(c) } // arguments iterator function getUserInfo (name, male, age, hobby) { console.log(arguments.length) for (const arg of arguments) { console.log(arg) } } getUserInfo(‘code monkey‘, ‘male‘, ‘28‘, ‘make money‘)
一個類數組對象如果要具備可被 for...of 循環調用的 Iterator 接口,就必須在Symbol.iterator的屬性上部署遍歷器生成方法。
// Array alike iterator const arrAlike = { 0: ‘a‘, 1: ‘b‘, 2: ‘c‘, length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] } for (const item of arrAlike) { console.log(item) // a b c } // 普通對象部署數組的Symbol.iterator方法,並無效果。 const obj = { a: ‘a‘, b: ‘b‘, c: ‘c‘, length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] } for (const item of obj) { console.log(item) // undefined undefined undefined }
註:普通對象部署數組的Symbol.iterator方法,並無效果。因為數組,Map和類數組對象等結構中的成員都是有順序的,
即都是線性的結構,而對象各成員並沒有一個確定的順序,所以遍歷時先遍歷誰後遍歷誰並不確定。
【解構、擴展運算 和 Iterator】
對數組和 Set 結構進行解構賦值時,會默認調用Symbol.iterator
方法。
擴展運算符(...)也會調用默認的 Iterator 接口。
const set = new Set().add(‘a‘).add(‘b‘).add(‘c‘) const [x, y] = set console.log(x, y) const [first, ...rest] = set console.log(first, rest) const str = ‘hello‘ console.log([...str]) const arr = [‘b‘, ‘c‘]; console.log([‘a‘, ...arr, ‘d‘])
【小結】
1. Iterator 就是為了提供一種統一的接口機制。任何的數據結構,只要部署了Iterator接口,便可以使用 for…of 來遍歷。
2. es6中有三類結構生來就具有Iterator接口:數組、類數組對象、Map和Set結構。
3. String、類數組對象、函數的arguments對象和nodeList 對象的遍歷以及generator的使用、擴展運算符和解構賦值的操作也會調用到 iterator。
4. 對象不具備 iterator 接口,但是可以通過部署 Symbol.iterator 屬性來使對象可遍歷。
ES6 中的 iterator