处理集合中的每个项是很常见的操作。JavaScript 提供了许多迭代集合的方法,从简单的for
循环到map()
和filter()
。迭代器和生成器将迭代的概念直接带入核心语言,并提供了一种机制来自定义for...of
循环的行为 。
更多详情,请参见:
一个迭代器对象 ,知道如何每次访问集合中的一项, 并跟踪该序列中的当前位置。在 JavaScript 中 迭代器是一个对象,它提供了一个next()
方法,用来返回序列中的下一项。这个方法返回包含两个属性:done
和 value
。
迭代器对象一旦被创建,就可以反复调用next()
。
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{done: true};
}
};
}
一旦初始化,next()
方法可以用来依次访问对象中的键值:
var it = makeIterator(['yo', 'ya']);
console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done); // true
虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。Generators提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。
GeneratorFunction 是一个可以作为迭代器工厂的特殊函数。当它被执行时会返回一个新的Generator对象。 如果使用function*
语法,则函数将变为GeneratorFunction。
function* idMaker() { var index = 0; while(true) yield index++; } var gen = idMaker(); console.log(gen.next().value); // 0 console.log(gen.next().value); // 1 console.log(gen.next().value); // 2 // ...
一个定义了迭代行为的对象,比如在for...of
中循环了哪些值。一些内置类型,如Array
或Map
具有默认的迭代行为,而其他类型(如Object
)没有。
为了实现可迭代,一个对象必须实现 @@iterator 方法,这意味着这个对象(或其原型链中的一个对象)必须具有带 Symbol.iterator
键的属性:
我们可以像这样实现自己的迭代:
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; for (let value of myIterable) { console.log(value); } // 1 // 2 // 3 //or console.log([...myIterable]); // [1, 2, 3]
String
,Array
,TypedArray
,Map
和 Set
都内置可迭代对象,因为它们的原型对象都有一个 Symbol.iterator
方法。
一些语句和表达式是预料会用于可迭代对象,例如 for-of
循环,spread syntax,yield*
和destructuring assignment.
for (let value of ['a', 'b', 'c']) { console.log(value); } // "a" // "b" // "c" [...'abc']; // ["a", "b", "c"] function* gen() { yield* ['a', 'b', 'c']; } gen().next(); // { value: "a", done: false } [a, b, c] = new Set(['a', 'b', 'c']); a; // "a"
生成器根据需求计算它们的产出值,这使得它们能够有效地表示计算成本高的序列,或者甚至如上所述的无限序列。
The next()
方法也接受可用于修改生成器内部状态的值。传递给next()
的值将被视为暂停生成器的最后一个yield
表达式的结果。
以下是使用 next(x)
重新启动 fibonacci 序列生成器:
function* fibonacci() { var fn1 = 0; var fn2 = 1; while (true) { var current = fn1; fn1 = fn2; fn2 = current + fn1; var reset = yield current; if (reset) { fn1 = 0; fn2 = 1; } } } var sequence = fibonacci(); console.log(sequence.next().value); // 0 console.log(sequence.next().value); // 1 console.log(sequence.next().value); // 1 console.log(sequence.next().value); // 2 console.log(sequence.next().value); // 3 console.log(sequence.next().value); // 5 console.log(sequence.next().value); // 8 console.log(sequence.next(true).value); // 0 console.log(sequence.next().value); // 1 console.log(sequence.next().value); // 1 console.log(sequence.next().value); // 2
你可以通过调用其throw()
方法强制生成器抛出异常,并传递应该抛出的异常值。这个异常将从当前挂起的生成器的上下文中抛出,就好像当前挂起的yield
是一个throwvalue
语句。
如果在抛出的异常处理期间没有遇到yield
,则异常将通过调用 throw()
向上传播,对 next()
的后续调用将导致done
属性为true
。
生成器具有return(value)
方法,返回给定的值并完成生成器本身。