1. 什么是 iterator
iterator 是一个定义在对象的 Symbol.iterator 属性上的方法执行时返回的对象, 常见在 Array, String , Map 等上
const arr = [1, 2, 3, 4]
console.log(arr[Symbol.iterator]()) //=> Array Iterator {}
当我们使用 ... 展开运算符去展开一个数组时, 其内部会使用到 iterator
当我们使用 for of 循环时, 其内部也会使用到 iterator ...
console.log(...arr) //=> 1 2 3 4
for (const item of arr) {
console.log(item)
//=> 1
//=> 2
//=> 3
//=> 4
}
然而, 没有 Symbol.iterator 方法的对象是无法使用这些功能的
const obj = {0: 1, 1: 2, 2: 3, 3: 4, length: 4}
console.log(obj[Symbol.iterator]) //=> undefined
console.log(...obj) // Uncaught TypeError: Found non-callable @@iterator
2. 如何使用 iterator
根据迭代器协议: 一个迭代器对象拥有 next() 方法, 且遵循如下语义
| next 的返回对象中的值 | 意义 |
|---|---|
| value | 迭代器所获取的序列的某一个值 |
| done | 如果通过迭代器能获取序列的下一个值, 则 done 为 true, 否则为 false |
既然是方法, 那么就应该可以执行
const it = arr[Symbol.iterator]()
console.log(it.next()) //=> {value: 1, done: false}
console.log(it.next()) //=> {value: 2, done: false}
console.log(it.next()) //=> {value: 3, done: false}
console.log(it.next()) //=> {value: 4, done: false}
console.log(it.next()) //=> {value: undefined, done: true}
3. 自己实现一个简单的 iterator 获取方法
const getArrayIterator = (array) => {
let cur = 0
let state = false
const myIterator = {
next() {
let done = cur >= array.length
let value = done ? undefined : array[cur ++]
return done ? {value: undefined, done: true} : { value, done }
// 当返回的对象中 done 为 false 时, 即使数组再添加元素
// 调用 next 方法也不会返回序列中的值了
},
// 返回的 iterator 对象也能够返回 iterator 对象, 可以使用 ... 和 for of
[Symbol.iterator]() {
return this
}
}
return myIterator
}
const it0 = getArrayIterator([1, 2, 3, 4, 5])
const it1 = getArrayIterator([2, 4, 6, 8, 10])
console.log(...it0) //=> 1 2 3 4 5
for (const item of it1) {
console.log(item)
//=> 2
//=> 4
//=> 6
//=> 8
//=> 10
}
4. generator
generator 是执行 generator function 时的返回值, 其是一个对象, 且遵循迭代器协议
每次对 generator 使用 next 方法
- 在
done不为true时, 其返回的对象中的value都对应一个yield的值 - 当
done为true时,value对应的是generator function中return所对应的值
// generator function
function * gen() {
yield 1
yield 2
return 4
}
console.log(...gen()) //=> 1 2
// generator object
const it = gen()
console.log(it.next()) //=> {value: 1, done: false}
console.log(it.next()) //=> {value: 2, done: false}
console.log(it.next()) //=> {value: 4, done: true}
5. 由 generator 引出的 next 方法的传参使用
第 n 次使用 next 时, 所传的参数会被当做第 n - 1 个 yield 的返回值(第一次调用 next 方法时传参并没有用)
function * gen() {
let a = yield 1
let b = yield 2
return a + b
}
let it = gen()
console.log(it.next()) //=> {value: 1, done: false}
console.log(it.next(10)) //=> {value: 2, done: false}
console.log(it.next(20)) //=> {value: 30, done: true}