迭代器和生成器
迭代器
在JS中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。更具体说,迭代器是通过使用next()方法实现Iterator protocol的任何一个对象,该方法返回具有两个属性的对象:value,这是序列中的next值;和done,如果已经迭代到序列中的最后一个值,则它为true。如果value和done一起存在,则它是迭代器的返回值。
一旦创建,迭代器对象可以重复调用next()方法显示的迭代,迭代一个迭代器被称为消耗了这个迭代器,因为它通常只能执行一次。在产生终止值之后,对next()的额外调用应该继续返回{done:true}
Javascript 中最常见的迭代器是 Array 迭代器,它只是按顺序返回关联数组中的每个值。虽然很容易想象所有迭代器都可以表示为数组,但事实并非如此。数组必须完整分配,但迭代器仅在必要时使用,因此可以表示无限大小的序列,例如 0 和无穷大之间的整数范围。
这是一个可以做到这一点的例子。它允许创建一个简单的范围迭代器,它定义了从开始(包括)到结束(独占)间隔步长的整数序列。它的最终返回值是它创建的序列的大小,由变量 iterationCount 跟踪。
1 | function makeRangeIterator(start = 0, end = Infinity, step = 1) { |
使用这个迭代器看起来像这样:
1 | let it = makeRangeIterator(1, 10, 2); |
生成器函数
虽然自定义的迭代器是一个有用的工具,但是由于需要显式维护内部状态,因此需要谨慎的创建。生成器函数提供了一个强大的选择:它允许你定义一个包含自由迭代算法的函数,同时它可以自动维护自己的状态。生成器函数使用fnunction*语法编写。最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。
可以根据需要多次调用该函数,并且每次都返回一个新的Generator,但每个Generator只能迭代一次。
我们现在可以调整上面的例子了。此代码的行为是相同的,但实现更容易编写和阅读。
1 | function* makeRangeIterator(start = 0, end = Infinity, step = 1) { |
可迭代对象
若一个对象拥有迭代行为,比如在for...of中会循环哪些值,那么这个对象就是一个可迭代对象。一些内置类型,如数组或者Map拥有默认的迭代行为,而其他类型(如Object)则没有。
为了实现可迭代,一个对象必须实现@@iterator方法,这意味着这个对象必须具有一个带symbol.iterator键的属性。
可以多次爹地啊一个迭代器,或者只迭代一次。程序员应该知道是哪种情况。只能迭代一次的 Iterables(例如 Generators)通常从它们的**@@iterator方法中返回它本身,其中那些可以多次迭代的方法必须在每次调用@@iterator**时返回一个新的迭代器。
自定义的可迭代对象
1 | var myIterable = { |
内置可迭代对象
String、Array、TypedArray、Map 和 Set 都是内置可迭代对象,因为它们的原型对象都拥有一个 Symbol.iterator 方法。
用于可迭代对象的语法
一些语句和表达式专用于可迭代对象,例如 for-of 循环,展开语法,yield* 和 解构赋值。
例如for...of循环这样的语言结构会利用symbol.iterator为键的函数执行迭代操作,循环时,他们会调用这个函数,并默认这个函数会返回一个实现迭代器API的对象。