主题
迭代器与生成器
可迭代协议
1. 可迭代对象
实现了 Iterable 接口的结构称为可迭代对象,Iterable 接口必需使用 Symbol.iterator 作为键,并为其引用一个迭代器工厂函数
2. 内置实现了 Iterable 接口的类型
字符串 | 数组 | 映射 | 集合 | arguments 对象 | NodeList 等 DOM 集合类型 |
3. 接受可迭代对象的原生语言特性
- for-of 循环
- 数组解构
- 扩展操作符
- Array.from()
- 创建映射
- 创建集合
- Promise.all() 接受由期约组成的可迭代对象
- Promise.race() 接受由期约组成的可迭代对象
- yield* 操作符,在生成器中使用
迭代器
1. IteratorResult 对象
迭代器使用 next() 方法在可迭代对象中遍历数据。调用 next() 会返回一个 IteratorResult 对象
typescriptinterface IteratorResult { value: any; // 本次迭代的值 done: boolean; // 是否已经迭代完成。true 表示迭代完成,false 表示还有下一次迭代 }
2. 特点
js
// 以下例子,全使用此迭代器
const arr = ['foo', 'bar']
const iter = arr[Symbol.iterator]()
只要迭代器到达 done: true 状态,后续调用 next() 就一直返回同样的值
jsiter.next() // { done: false, value: 'foo' } iter.next() // { done: false, value: 'bar' } iter.next() // { done: true, value: undefined } iter.next() // { done: true, value: undefined }
每个迭代器都表示对可迭代对象的一次性有序遍历。不同迭代器之间没有任何关联
jsconst iter1 = arr[Symbol.iterator](), iter2 = arr[Symbol.iterator]() iter1.next() // { done: false, value: 'foo' } iter2.next() // { done: false, value: 'foo' }
迭代器使用游标来记录可迭代对象的历程。所以如果可迭代对象在迭代期间被修改,迭代器也会反映其变化
jsiter.next() // { done: false, value: 'foo' } arr.splice(1, 0, 'baz') iter.next() // { done: false, value: 'baz' } iter.next() // { done: false, value: 'bar' }
3. 自定义迭代器
js
const obj = {
'a': 1,
'b': 2,
'c': 3
}
obj[Symbol.iterator] = function() {
const that = this,
keys = Object.keys(this),
len = keys.length
let count = 0
return {
next() {
return count < len ?
{ value: that[keys[count++]], done: false } :
{ value: undefined, done: true }
}
}
}
for (let item of obj) {
console.log(item) // 1 2 3
}
4. 提前终止迭代器
for-of 循环通过 break、continue、return 或 throw 提前退出
解构操作并未消费所有值
可以定义 return() 方法指定对迭代器提前关闭时执行的逻辑
js
const obj = {
'a': 1,
'b': 2,
'c': 3
}
obj[Symbol.iterator] = function() {
const that = this,
keys = Object.keys(this),
len = keys.length
let count = 0
return {
next() {
return count < len ?
{ value: that[keys[count++]], done: false } :
{ value: undefined, done: true }
},
return() {
console.log('提前结束')
return { value: undefined, done: true }
}
}
}
const [a, b] = obj // 提前结束
生成器
1. 创建生成器
在一个函数名称前面加一个星号( * )表示创建,星号不受两侧空格影响
箭头函数不能用来定义生成器函数
2. 执行过程
- 调用生成器函数产生一个生成器对象,生成器对象一开始处于暂停执行的状态
- 生成器对象调用 next() 方法,则开始正常执行,直到遇到 yield 关键字,对 yield 后面的表达式计算,确定返回值。next() 方法返回一个 IteratorResult 对象,
{ done: false, value: 表达式返回值 }
- 再次调用 next() 方法,得到下一个 yield 的状态
- 直到遇到 return 关键字,对 return 后面的表达式计算,确定返回值。返回一个 IteratorResult 对象,
{ done: true, value: 表达式返回值 }
js
function* func () {
console.log('start')
yield 'foo' + 'bar'
return 'baz'
}
const iter = func()
let result = iter.next() // start
console.log(result) // { done: false, value: 'foobar' }
result = iter.next()
console.log(result) // { done: true, value: 'baz' }
3. yield 的输入输出
yield 表达式
会被替换成其返回状态的 next() 的第一个参数
js
function* func(x) {
const y = yield x + 1
const z = yield y + 1
return z
}
const iter = func(0)
let result = iter.next()
console.log(result) // { done: false, value: 1 }
result = iter.next(result.value)
console.log(result) // { done: false, value: 2 }
result = iter.next(result.value)
console.log(result) // { done: true, value: 2 }
4. 简化迭代器
生成器对象实现了 Iterable 接口,会产生迭代器
js
const obj = {
'a': 1,
'b': 2,
'c': 3
}
obj[Symbol.iterator] = function* () {
for (const item of Object.keys(this)) {
yield item
}
}
for (const item of obj) {
console.log(item) // 1 2 3
}
5. 使用 yield* 迭代一个可迭代对象
js
function* func() {
yield* [1, 2]
yield* [3, 4]
}
for (const item of func()) {
console.log(item) // 1 2 3 4
}
js
const obj = {
'a': 1,
'b': 2,
'c': 3
}
obj[Symbol.iterator] = function* () {
yield* Object.keys(this)
}
for (const item of obj) {
console.log(item) // 1 2 3
}
for-await-of
实现了 [Symbol.asyncIterator]
,与迭代器类型,区别在于异步迭代器的 next 方法 返回一个 Promise 对象
js
function buildAsync (time) {
return new Promise(resolve => {
setTimeout(() => resolve(time), time)
})
}
async function run(){
const arr = [buildAsync(1000),buildAsync(3000),buildAsync(2000)]
for await (const time of arr){
console.log(time)
}
}
run()
// 1 秒后输出 1000
// 2 秒后输出 3000, 因为并行执行
// 马上输出 2000,因为并行执行,所以已经异步执行完了,但 for-await-of 有顺序性,所以还是最后输出