处理集合中的每个项是很常见的操作。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)
方法,返回给定的值并完成生成器本身。