处理集合中的每个项是很常见的操作。JavaScript 提供了许多迭代集合的方法,从简单的for循环到map()filter()。迭代器和生成器将迭代的概念直接带入核心语言,并提供了一种机制来自定义for...of循环的行为 。

更多详情,请参见:

迭代器

一个迭代器对象 ,知道如何每次访问集合中的一项, 并跟踪该序列中的当前位置。在  JavaScript 中 迭代器是一个对象,它提供了一个next() 方法,用来返回序列中的下一项。这个方法返回包含两个属性:donevalue

迭代器对象一旦被创建,就可以反复调用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中循环了哪些值。一些内置类型,如ArrayMap具有默认的迭代行为,而其他类型(如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

[...myIterable]; // [1, 2, 3]

内置可迭代性

StringArrayTypedArrayMapSet 都内置可迭代性,因为它们的原型对象都有一个 Symbol.iterator 方法。

使用可迭代值的语句

一些语句和表达式接受可迭代值,例如 for-of 循环,spread syntaxyield* 和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) 方法,返回给定的值并完成生成器本身。

文档标签和贡献者

 最后编辑者: zhangjiawei0,