ES6(ECMAScript 2015)中提出了生成器的概念,进一步完整了JavaScript语言。 本文介绍了可迭代协议与迭代器协议的概念区别,以及生成器的声明与使用方法。

生成器函数

生成器函数是用来返回生成器的函数,生成器是一种有状态的迭代器, 可实现较复杂的迭代行为,比如生成ID。 生成器函数使用function*语法来定义:

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

生成器函数也可以通过GeneratorFunction(类似Function)、function* expression来定义(可以使用匿名函数)。

调用生成器函数并不会执行函数体,而是会返回一个生成器

var gen = idMaker();

生成器

ES6中生成器有三个方法:

  • Generator.prototype.next(): 返回下一个yield的值。
  • Generator.prototype.return(): 返回并结束生成器。
  • Generator.prototype.throw(): 抛出错误。

示例代码:

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

可迭代协议

可迭代协议(iterable protocol)使得我们可以定制JavaScript对象 的迭代行为,比如定义for...of时迭代出来怎样的值。 将[Symbol.iterator]属性定义为一个迭代器对象即可实现该协议。

String, Array, Map等内置类型是满足可迭代协议的,例如:

var someString = "hi";
var iterator = someString[Symbol.iterator]();
iterator.next();                             // { value: "h", done: false }
iterator.next();                             // { value: "i", done: false }
iterator.next();                             // { value: undefined, done: true }

迭代器协议

迭代器协议(iterator protocol)又称生成器协议, 该协议定义了什么是迭代器对象。其实迭代器协议很简单, 只要实现.next()方法(并具有对应语义)即可。 该方法返回的对象除.value属性外, 还应有一个.done属性来标识迭代器是否已越过最后一个元素:

function* g(){
    yield 1;
    yield 2;
}
var iterator = g();
console.log(iterator.next());   // { value: 1, done: false }
console.log(iterator.next());   // { value: 2, done: false }
console.log(iterator.next());   // { value: undefined, done: true }

生成器函数中的return会立即结束生成器,因此done会立即变为true(不同于yield)。

function* g(){
    yield 1;
    return 2;
}
var iterator = g();
console.log(iterator.next());   // { value: 1, done: false }
console.log(iterator.next());   // { value: 2, done: true }
console.log(iterator.next());   // { value: undefined, done: true }

yield*

yield*可以将需要yield的值委托给另一个生成器,或其他任何可迭代对象 (由ES6 迭代协议规约)。例如:

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var iterator = g2();

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

除了生成器外,yield*还可以委托给其他的可迭代类型:

function* g() {
  yield* [1, 2];
  yield* "34";
  yield* arguments;
}

var iterator = g(5, 6);

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

参考阅读

文中代码来自MDN

本文采用 知识共享署名 4.0 国际许可协议(CC-BY 4.0)进行许可,转载注明来源即可: https://harttle.land/2016/08/02/es6-generator.html。如有疏漏、谬误、侵权请通过评论或 邮件 指出。