简要描述:
- 一种接口机制,为各种不同的数据结构提供统一访问的机制。
- 主要供for...of消费。
- 一句话 :不支持遍历的数据结构“可遍历”。
原生具备 Iterator 接口的数据结如下,可通过for...of进行遍历:
Array、Map、Set、String、TypedArray(用于二进制数据的)、函数的arguments对象、NodeList对象
1. 模拟一个iterator执行
function makeIterator(arr) {
let nextIndex = 0;
return {
next() {
return nextIndex < arr.length ?
{ value: arr[nextIndex++], done: false } : { value: undefined, done: true }
}
}
}
let it = makeIterator(['a', 'b', 'c'])
console.log(it.next()); //{value: 'a', done: false}
console.log(it.next()); //{value: 'b', done: false}
console.log(it.next()); //{value: 'c', done: false}
console.log(it.next()); //{value: undefined, done: true}
以上是不是让你想起了Generator函数,调用方式也是通过next一次一次调,打印的数据结构也一样,Generator函数内部封装了迭代器。
2. 拿数组举例
let arr = [1, 2, 3]
for (let key of arr) {
console.log(key)
}
console.log(Symbol.iterator) //Symbol(Symbol.iterator)
let ite = arr[Symbol.iterator]();
console.log(ite) //Array Iterator {}
console.log(ite.next()) //{value: 1, done: false}
console.log(ite.next()) //{value: 2, done: false}
console.log(ite.next()) //{value: 3, done: false}
console.log(ite.next()) //{value: undefined, done: true}
控制台中打印arr,展开[[prototype]],发现有个Symbol(Symbol.iterator)属性,这个就是它的迭代器,就是数组能用for...of遍历的原因
image.png
想要数据能够用for...of进行遍历,就要给数据提供Iterator迭代器。
3. 为数据自定义迭代器
迭代器的条件
- 可迭代协议:当前对象是否有 Symbol.iterator 属性
- 迭代器协议:当前迭代器必须符合条件
return {
next() {
return { value: ... , done: ... }
}
}
接下来,让我们为下面的courses自制迭代器 ,让courses能够用for...of遍历到每一个数组内的元素
let courses = {
allCources: {
frontend: ['ES', '小程序', 'Vue', 'React'],
backend: ['Java', 'Python'],
webapp: ['Android', 'IOS']
}
}
courses[Symbol.iterator] = function () { //定义Symbol(Symbol.iterator)属性,值为iterator函数
let allCources = this.allCources;
let keys = Reflect.ownKeys(allCources) //取到数组的属性名,包括Symbol
let values = [];//所有元素放到这里
return {
next() {
if (!values.length) {
if (keys.length) { //没有
// 往values里添加数据,确保每次的next调用,都能return出values中的值
values = allCources[keys[0]]; //keys每次从0开始取,取过的就shift删掉,避免取重复的
keys.shift();
}
}
return {
done: !values.length, //values要是空了,代表都取过了,返回true
value: values.shift() //return出values中的值
}
}
}
}
for (let key of courses) {
console.log(key)
}
// 打印结果:ES 小程序 Vue React Java Python Android IOS
4. 异步迭代器
- Symbol.asyncIterator
- for await (...of...)
function getPromise(time) { //异步函数
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ value: time, done: false }) //next返回值
}, time)
})
}
let arr = [getPromise(1000), getPromise(2000), getPromise(3000)];//要被遍历的数组,数组元素都是异步函数
arr[Symbol.asyncIterator] = function () { //异步迭代器
let nextIndex = 0;
return {
next() { //返回值为 在同步的返回值写法上包裹一层promise
return nextIndex < arr.length ? arr[nextIndex++] : Promise.resolve({ value: undefined, done: true })
}
}
}
async function test(){
//for await(... of ...)等到当前元素完成再遍历下一个元素,并且遍历到的是异步完成的结果值
for await (let item of arr){ //遍历到异步的结果
console.log(item) //1000 2000 3000
}
}
test(); //调用异步函数