迭代器和生成器(function* & yield)

参考:

处理一个集合中每一项是很常见的操作。JavaScript 提供了好几种方法来遍历一个集合,从简单的 for 和 for each 循环至 map(),filter() 和 array comprehensions。 迭代器和生成器,在 JavaScript 1.7 中, 迭代器的概念属于核心语言中的重点,并提供了一种机制来定义 for…of 循环。

迭代器

迭代器是一个对象,知道如何存取物品从一个集合一次取出一项, 而跟踪它的当前序列所在的位置。在 JavaScript 中 迭代器是一个对象,它提供了一个 next() 方法返回序列中的下一个项目。当这个序列消亡时这个方法可以随时抛出一个StopIteration exception(异常)。

一旦创建,迭代器对象可以显式地通过不断调用 next(),或隐式地使用 JavaScript 的 for…in 和 for each 构造。

简单的迭代器对象和数组可以使用 Iterator() 函数创建(下面是一个简单的对像)。

生成器(Generators): 一个更好的方法来构建遍历器EDIT

虽然迭代器是一个有用的工具,但是由于需要显示地维持他们的内部状态,所以他们的构造需要仔细的规划(看得我眼花呀)。生成器给你提供了一个强大的选择:它允许你通过写一个可以保存自己状态的的简单函数来定义一个迭代算法。

一个生成器其实是一种特殊类型的函数(这个函数作为一个为迭代器工作的工厂),一个函数如果它里面包含了一个或一个以上的yield表达式,那么这个函数就成为一个生成器了。

当一个生成器函数被一个函数体调用时并不会马上执行;相反,它会返回一个generator-iterator对象。每次调用generator-iterator的next()方法将会执行这个函数体至下一个yield表达式并且返回一个结果。当函数执行完或者执行到一个return时抛出一个StopIteration exception异常。

yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

let generator = function* () {
  yield 1;
  yield* [2,3,4];
  yield 5;
};

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

高级生成器

生成器按照要求产生values,这样可以高效的表示序列,这点对于计算机来说很重要,上面的无穷序列就是一个很好的证明。

除了next()方法,generator-iterator对象也有一个send()方法可以用来修改生成器的内部状态。传递一个值给send()将被视为最后一个yield表达式的结果(生成器暂停)。你必须在你使用send(参数)前通过调用next()来启动生成器。

这里是斐波那契生成器使用send()来重新启动序列:

function fibonacci(){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    var reset = yield current;
    if (reset){
        fn1 = 1;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
print(sequence.next());     // 1
print(sequence.next());     // 1
print(sequence.next());     // 2
print(sequence.next());     // 3
print(sequence.next());     // 5
print(sequence.next());     // 8
print(sequence.next());     // 13
print(sequence.send(true)); // 1
print(sequence.next());     // 1
print(sequence.next());     // 2
print(sequence.next());     // 3

生成器拥有一个 close() 方法来强制生成器结束。结束一个生成器会产生如下影响:

  • 所有生成器中有效的 finally 字句将会执行
  • 如果 finally 字句抛出了除 StopIteration 以外的任何异常,该异常将会被传递到 close() 方法的调用者
  • 生成器会终止

function*

function* 声明(function关键字后跟一个星号)定义一个generator(生成器)函数,返回一个Generator对象。

描述

生成器是一种可以从中退出并在之后重新进入的函数。生成器的环境(绑定的变量)会在每次执行后被保存,下次进入时可继续使用。

调用一个生成器函数并不马上执行它的主体,而是返回一个这个生成器函数的迭代器(iterator)对象。当这个迭代器的next()方法被调用时,生成器函数的主体会被执行直至第一个yield表达式,该表达式定义了迭代器返回的值,或者,被 yield*委派至另一个生成器函数。next()方法返回一个对象,该对象有一个value属性,表示产出的值,和一个done属性,表示生成器是否已经产出了它最后的值。

示例

简单示例

function* idMaker(){
  var index = 0;
  while(index<3)
    yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined
// ...

yield*的示例

function* anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function* generator(i){
  yield i;
  yield* anotherGenerator(i);
  yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20

yield

yield 关键字用来暂停和继续一个生成器函数 (function* or legacy generator).

yield [[expression]];

expression
用作返回值. 如果忽略, 将返回 undefined .