ES6中的生成器函数是什么?

我们知道 JavaScript 函数是从上到下执行的,但 ES6 在2016年6月发布时,这一切都改变了,它带来了在执行过程中暂停函数的能力,又能从暂停处继续执行。

这是怎么做到的呢?Generator 也称为生成器函数。

官方的介绍生成器函数提供了一个强大的选择,它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用 function* 语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为 Generator 的迭代器。 通过调用生成器的下一个方法消耗值时,Generator 函数将执行,直到遇到 yield 关键字。

生成器函数可以在中间暂停一次或多次,之后可以重新恢复。当标准函数被调用时,控制将由被调用函数控制,直到它返回为止,但是生成器函数允许调用函数控制被调用函数的执行。如下图示意:

普通函数VS生成器函数

生成器函数关键的三个概念:

  • function* :用于将函数声明为生成器函数,比普通的函数声明多了一个 ** 位置可以在 function 关键字旁边,也可以在函数名旁边。
  • yield :只能出现在生成器函数中,但是生成器也可以没有 yield 关键字,函数遇到 yield 时会暂停并抛出它后面的表达式的结果。
  • next :将代码的控制权返回给生成器函数。

下面来看一个基本的实例。

创建生成器函数

JavaScript 中的生成器函数的创建与其他函数非常相似,除了语法上的一点差异,如下:

function* citySolitaire() {
    yield "北京市";
    yield "上海市";
    yield "深圳市";
    yield "广州市";
    yield "天津市";
}

区别在于函数定义本身,生成器函数是使用 function* 语法声明的。

yield 关键字

当一个生成器函数被调用时,它会一直执行直到遇到一个 yield 表达式。此时,生成器函数暂停,返回 yield 关键字后定义的值,等待继续执行,直到再次调用该函数。

返回值

JavaScript 中生成器函数和常规函数之间的另一个区别:生成器函数在封装的对象中返回其值。从技术上讲,生成器符合迭代器协议(如 Maps 和 Observables),这意味着封装的对象看起来像这样:

{
   value, // 从生成器函数获取的下一个值
   done; // 一个标志,标记是否是序列中的最后一个值
}

如前面定义的生成器函数 citySolitaire ,从中获取其值,如下:

const city = citySolitaire();
console.log(city.next()); // { value: '北京市', done: false }

每次调用 next() 方法时,都会以前面提到的格式返回一个对象。要获取数据,只需访问 value 属性。当 done 属性的值为 false 时,就意味着还有更多的数据需要检索,可以再次调用生成器。如下:

const city = citySolitaire();
console.log(city.next()); // { value: '北京市', done: false }
console.log(city.next()); // { value: '上海市', done: false }
console.log(city.next()); // { value: '深圳市', done: false }
console.log(city.next()); // { value: '广州市', done: false }
console.log(city.next()); // { value: '天津市', done: false }
console.log(city.next()); // { value: undefined, done: true }

正如上面执行的结果,当执行到最后一个的时候再次执行 next() ,就会返回期望的 donetrue

总结

JavaScript生成器函数是不很奇妙,通过本文可以简单了解一下什么是生成器函数,至于能够做什么?请听下回分解!