Skip to content

迭代器与生成器

可迭代协议

1. 可迭代对象

实现了 Iterable 接口的结构称为可迭代对象Iterable 接口必需使用 Symbol.iterator 作为键,并为其引用一个迭代器工厂函数

2. 内置实现了 Iterable 接口的类型
字符串数组映射集合arguments 对象NodeListDOM 集合类型
3. 接受可迭代对象的原生语言特性
  • for-of 循环
  • 数组解构
  • 扩展操作符
  • Array.from()
  • 创建映射
  • 创建集合
  • Promise.all() 接受由期约组成的可迭代对象
  • Promise.race() 接受由期约组成的可迭代对象
  • yield* 操作符,在生成器中使用

迭代器

1. IteratorResult 对象
  • 迭代器使用 next() 方法在可迭代对象中遍历数据。调用 next() 会返回一个 IteratorResult 对象

    typescript
    interface IteratorResult {
     value: any; // 本次迭代的值
     done: boolean; // 是否已经迭代完成。true 表示迭代完成,false 表示还有下一次迭代
    }
2. 特点
js
// 以下例子,全使用此迭代器
const arr = ['foo', 'bar']
const iter = arr[Symbol.iterator]()
  1. 只要迭代器到达 done: true 状态,后续调用 next() 就一直返回同样的值

    js
    iter.next() // { done: false, value: 'foo' }
    iter.next() // { done: false, value: 'bar' }
    iter.next() // { done: true, value: undefined }
    iter.next() // { done: true, value: undefined }
  2. 每个迭代器都表示对可迭代对象的一次性有序遍历。不同迭代器之间没有任何关联

    js
    const iter1 = arr[Symbol.iterator](),
          iter2 = arr[Symbol.iterator]()
    
    iter1.next() // { done: false, value: 'foo' }
    iter2.next() // { done: false, value: 'foo' }
  3. 迭代器使用游标来记录可迭代对象的历程。所以如果可迭代对象在迭代期间被修改,迭代器也会反映其变化

    js
    iter.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 循环通过 breakcontinuereturnthrow 提前退出

  • 解构操作并未消费所有值

  • 可以定义 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 有顺序性,所以还是最后输出