关于Generator的误解
生成器(Generator)一词在JavaScript中常常受到误解,一些细微的概念差别的积累,慢慢导致了不少理解上的混淆。它时而在指一个函数,时而又在代指迭代器(iterator)。那么它在概念上到底是指什么?
去前些时候在团队里做了一个redux-saga源码浅读的分享,由于redux-saga的实现机制核心就是Generator,为了写PPT我打算去抄个比较正式的定义,然后就又去翻了一下手边的两本参考书,阮一峰的《ES6入门指南》和Zakas的《深入理解ES6》,不过这次阅读却很快发现了后者的内容存在翻译上的错误。
ECMAScript6同时还引入了个生成器对象,它可以让创建迭代器对象的过程变得更简单。--深入理解ES6
这上面是摘抄自深入理解ES6里的中文版原文,乍看之下好像没什么问题,但其实严格意义上讲是错的,“生成器对象”概念上错了,因为联系上下文,创建迭代器对象的是“生成器函数”。难道Zakas写错了?我特意翻查了英文原版:
Fortunately, ECMAScript 6 also provides generators, which make creating iterator objects much simpler.--Understanding ES6 By Zakas
原文是generators,所以原文没有错。想是译者在处于自身习惯上添加了些词,虽然没什么大碍,但对于严谨的教科书来说,这个概念混淆是不合适的。而事实上,生成器对象一词是存在的,但意义完全不同。MDN上的关于生成器对象的定义是很精确的:
The Generator object is returned by a generator function and it conforms to both the iterable protocol and the iterator protocol.
生成器函数(generator function)返回的是生成器对象(generator object),而因为每个生成器对象都符合可迭代协议和迭代器协议,所以每个生成器对象都是迭代器(当然反过来就不是,迭代器是个更为广泛的概念)。
所以ES6引入的是生成器函数(generator function),它使得创建迭代器对象(generator object)变得简单。当然Zakas还是简略了一些,用generator指代了generator function,这是常规做法,但他的文章里的词还是非常统一的,他文中的generator都指代generator funciton。
这点阮一峰老师就丝毫不含糊了,全程的用词是generator函数看似啰嗦,概念上却没有丝毫偏差。
还没开始写generator,但感觉我已经唠叨了一大堆了。那么下面列一下这些容易混淆的概念。
- Generator function: 返回生成器对象的函数,本身是一个生成数据的状态机。
- Generator object:生成器函数返回的对象,本身是一个迭代器。
- Iterrator: 符合迭代过程接口定义的对象
- Generator: 根据上下文,即可指生成器函数,也可能指对象(错误用法)。狭义仅指代生成器函数。
- Generator expression: 生成器表达式等同于生成器对象
最后这个生成器表达式JavaScript并不支持,后续ES版本里也没有提及。而我放在这里的主要目的是解释generator本身。generator并非JavaScript独有,在Python里早已是广泛使用的语法。当然由于我接触的语言也并不多到10多种,所以其他语言情况并不清楚。但就个人过去学过一些Python的经验而言,JavaScript的generator与Python中的generator并无二致,概念一致,且Python还支持生成器表达式。
关于generator的问题都可以在Python中找到答案,可以在Python文档里找到详细的术语generator的定义,甚至能在Python增强议案里找到为什么要用yield关键字之类的讨论。
所以generator究竟做什么用的?如果回归本意,generator是用来生成数据的。generator并非因为生成了迭代器而被称为生成器函数,而是因为它从机制上生成了数据,从而独占了这样一个宽泛的名字——不然它完全可以叫做迭代生成器么:) 当一系列数据可以通过迭代计算得出,那么我们就不需要读取加载整个数据,而只需去获取生成出来的数据。
当然JavaScript里我们更多的是把generator当作一个状态机来使用,并方便的处理异步流程。但显然状态本身也是数据,我们生成了状态并通过迭代器去控制流程,这个generator的用法还是颇为骚气的。最后列举一下自己对JavaScript generator的理解(可能并不准确):
- 一边迭代一边计算并生成数据的机制
- 一种以函数形式表达的生成数据的数据结构
- 一个可控(暂停,继续)的状态机
- 迭代器对象的生成函数
好久不写这么长,有点疲劳(笑)。由于本篇概念性比较强,难免偏漏,如有错误欢迎指出(当然留言需要翻墙)。虽然不知道还有多少人会看到,总之谢谢各位。