« 上一页下一页 »

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

更多详情，请参见:

- 迭代协议
- `for...of`
- `function*` 和 `Generator`
- `yield` 和 `yield*`

## 迭代器

一个迭代器对象 ，知道如何每次访问集合中的一项， 并跟踪该序列中的当前位置。在 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)` 方法，返回给定的值并完成生成器本身。

« 上一页下一页 »