JavaScript 迭代器和生成器
ECMAScript 6 规范新增的两个高级特性:生成器和迭代器能够使我们高效、清晰、方便的实现迭代。
在 ECMAScript 较早的版本中,执行迭代必须使用循环或其他辅助结构。随着代码量增加,代码会变得越发混乱。很多语言都通过原生语言结构解决了这个问题,开发者无须事先知道如何迭代就能实现迭代操作。这个解决方案就是迭代器模式。Python、Java、C++,还有其他很多语言都对这个模式提供了完备的支持。
生成器和生成器对象
生成器是 ECMAScript 6 新增的一个极为灵活的结构,拥有在一个函数块内暂停和恢复代码执行的能力。
需要注意的是:箭头函数不能用来定义生成器函数。(箭头函数的作用域根据当前环境决定)
通过生成器函数可以生成一个遵循可迭代协议(Iterable Protocol)和迭代器协议(Iterator Protocol)的生成器对象。
- 可迭代协议是指:支持迭代的自我识别能力和创建实现
Iterator接口的对象的能力。在ECMAScript中,这意味着必须暴露一个属性作为 “默认迭代器”,而且这个属性必须使用特殊的[Symbol.iterator]作为键。这个默认迭代器属性必须引用一个迭代器工厂函数,调用这个工厂函数必须返回一个新迭代器。 - 迭代器协议是指:含有
next()方法可以在可迭代对象中遍历数据,并返回{ done: Boolean, value: any }形式的返回数据,done用来标识迭代是否完成。
yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 yield 关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数只能通过在生成器对象上调用 next() 方法来恢复执行:
1 | function* generatorFun() { |
生成器对象遵循可迭代协议和迭代器协议,因此它可以通过 for...of 进行迭代
1 | function* generatorFun() { |
也可以通过 spread 语法输出其值
1 | function* generatorFun() { |
通过 yield* 可以将执行委托给另一个迭代器,是将一个生成器的流插入到另一个生成的流的方式
1 | function* generateSquence(start, end) { |
yield 不仅可以向外返回结果,还可以将外部的值传递到生成器内部(第一次调用 next() 方法传递的参数会被忽略)
1 | function* gen() { |
可以通过 generator.throw() 向生成器内部传递一个错误
1 | function* gen() { |
当迭代值是以异步的形式出现时,就需要使用异步迭代,通过为对象添加 [Symbol.asyncIterator] 方法返回异步迭代器对象,异步迭代器对象通过 for await...of 循环来访问迭代值
1 | let range = { |
迭代器
JavaScript 中的可迭代对象是对数组的泛化,可以通过 for...of 对其进行迭代。
迭代器是一种一次性使用的对象,用于迭代和它关联的可迭代对象。
Symbol.iterator
可以通过为对象添加一个返回迭代器的 [Symbol.iterator] 方法来使得对象成为可迭代的 – 迭代器是指一个拥有 next() 方法的对象。
通过 for...of 对可迭代对象进行迭代时,for...of 希望获得下一个迭代值时就会调用迭代器对象的 next() 方法,next() 方法返回的对象必须是{ value: any: done: Boolean } 形式的值,当 done 为 true 时表示迭代结束,否则表示还可以迭代下一个值。
一般来说,迭代器对象和要迭代的对象是不同的,这样可以避免迭代时造成要迭代对象的属性变化。
1 | const range = { |
使用 for...of 循环对对象进行迭代时,它会寻找 [Symbol.iterator] 方法,如果没有找到则会报错。
1 | const range = { |
如果需要在迭代过程中做更多的事情,我们可以通过显示调用迭代器来获得比 for...of 跟多的控制权
1 | const range = { |
JavaScript 中的内建可迭代对象
JavaScript 中很多内建对象都遵循了可迭代协议:
字符串
数组
映射(
Map/WeakMap)集合(
Set/WeakSet)arguments对象NodeList等DOM集合类型
数组和字符串
数组和字符串是使用最广泛的内建可迭代对象。
1 | const string = 'Best wishes for the beautiful world.' |
类数组对象和可迭代对象
JavaScript 中的类数组对象是指拥有 length 属性和索引的对象,但它不一定是可迭代的。
1 | const arrLikeObj = { |
JavaScript 中的可迭代对象是指实现了 [Symbol.iterator] 方法的对象。
我们可以通过全局方法 Array.from 将类数组对象转变成真正的数组再进行迭代
1 | const arrLikeObj = { |
使用生成器进行迭代
根据上文对生成器的介绍,很容易想到可以使用生成器函数来返回可迭代对象实现对象的迭代。
1 | const range = { |
提前终止迭代器
可选的 return() 方法用于指定在迭代器提前关闭时执行的逻辑。执行迭代的结构在想让迭代器知道它不想遍历到可迭代对象耗尽时,就可以 “关闭” 迭代器。可能的情况包括:
for...of循环通过break、continue、return或throw提前退出- 解构操作并未消费所有值
JavaScript 迭代器和生成器
https://cocoalei.github.io/blogs/2019/06/11/JavaScript 迭代器和生成器/