From 2556bf89627fa823c7437d4951327d4a79f025d2 Mon Sep 17 00:00:00 2001 From: Ruan Yifeng Date: Tue, 30 Dec 2014 09:29:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9Readme=E5=92=8Cgenerator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/generator.md | 37 +++++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0fe044b..1c34d93 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 本书为中级难度,适合已有一定JavaScript语言基础的读者,用来了解这门语言的最新发展;也可当作参考手册,查寻新增的语法点。 -全书已由电子工业出版社出版([版权页](images/copyright.png),[内页1](images/page1.png),[内页2](images/page2.png)),铜版纸全彩印刷,附有索引。电子版与纸版的内容是一致的,如果您对本书感兴趣,建议考虑购买纸版。这样可以使出版社不因出版开源书籍而亏钱,进而鼓励更多的作者开源自己的书籍。 +全书已由电子工业出版社出版([版权页](images/copyright.png),[内页1](images/page1.png),[内页2](images/page2.png)),铜版纸全彩印刷,附有索引。纸版是根据电子版排印的,内容截止到2014年10月,感谢张春雨编辑支持我将全书开源的做法。如果您对本书感兴趣,建议考虑购买纸版。这样可以使出版社不因出版开源书籍而亏钱,进而鼓励更多的作者开源自己的书籍。 - [京东](http://item.jd.com/11526272.html) - [当当](http://product.dangdang.com/23546442.html) diff --git a/docs/generator.md b/docs/generator.md index d913c81..a560a3e 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -1,6 +1,8 @@ # Generator 函数 -## 含义 +## 语法 + +### 简介 所谓Generator,有多种理解角度。首先,可以把它理解成一个内部状态的遍历器,每调用一次,内部状态发生一次改变(可以理解成发生某些事件)。ES6引入Generator函数,作用就是可以完全控制内部状态的变化,依次遍历这些状态。 @@ -72,7 +74,7 @@ setTimeout(function () { 上面代码中,函数f如果是普通函数,在为变量generator赋值时就会执行。但是,函数f是一个Generator函数,就变成只有调用next方法时,函数f才会执行。 -## next方法的参数 +### next方法的参数 yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。 @@ -120,7 +122,7 @@ it.next(13) 注意,由于next方法的参数表示上一个yield语句的返回值,所以第一次使用next方法时,不能带有参数。V8引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。 -## for...of循环 +### for...of循环 for...of循环可以自动遍历Generator函数,且此时不再需要调用next方法。 @@ -165,7 +167,7 @@ for (let n of fibonacci()) { 从上面代码可见,使用for...of语句时不需要使用next方法。 -## throw方法 +### throw方法 Generator函数还有一个特点,它可以在函数体外抛出错误,然后在函数体内捕获。 @@ -245,7 +247,7 @@ function* g(){ ``` -## yield*语句 +### yield*语句 如果yield命令后面跟的是一个遍历器,需要在yield命令后面加上星号,表明它返回的是一个遍历器。这被称为yield*语句。 @@ -357,7 +359,7 @@ result ``` -## 作为对象属性的Generator函数 +### 作为对象属性的Generator函数 如果一个对象的属性是Generator函数,可以简写成下面的形式。 @@ -383,7 +385,9 @@ let obj = { ``` -## Generator与状态机 +## 含义 + +### Generator与状态机 Generator是实现状态机的最佳结构。比如,下面的clock函数就是一个状态机。 @@ -417,6 +421,16 @@ var clock = function*(_) { 上面的Generator实现与ES5实现对比,可以看到少了用来保存状态的外部变量ticking,这样就更简洁,更安全(状态不会被非法篡改)、更符合函数式编程的思想,在写法上也更优雅。Generator之所以可以不用外部变量保存状态,是因为它本身就包含了一个状态信息,即目前是否处于暂停态。 +### Generator与协程 + +协程(coroutine)是一种程序运行的方式。传统的“子例程”(subroutine)采用堆栈式“后进先出”的执行方式,只有当调用的子函数完全执行完毕,才会结束执行父函数。协程与其不同,多个函数可以并行执行,但是只有一个函数处于正在运行的状态,其他函数都处于暂停态(suspended),函数之间可以交换执行权。也就是说,一个函数执行到一半,可以暂停执行,将执行权交给另一个函数,等到稍后收回执行权的时候,再恢复执行。这种可以并行执行、交换执行权的函数,就称为协程。 + +不难看出,协程适合用于多任务运行的环境。在这个意义上,它与线程很相似,都有自己的执行上下文、可以分享全局变量。它们的不同之处在于,同一时间可以有多个线程处于运行状态,但是运行的协程只能有一个,其他都处于暂停状态。此外,线程是抢先式的,到底哪个线程优先得到资源,必须由运行环境决定,但是协程是合作式的,执行权由协程自己分配。 + +Generator函数是ECMAScript 6对协程的实现,但属于不完全实现,只做到了暂停执行和转移执行权,有一些特性没有实现,比如不支持所调用的函数之中的yield语句。 + +如果将Generator函数看作多任务运行的方式,存在多个进入点和退出点。那么,一方面,并发的多任务可以写成多个Generator函数;另一方面,继发的任务则可以按照发生顺序,写在一个Generator函数之中,然后用一个任务管理函数执行(参考本节的“控制流管理”部分)。 + ## 应用 Generator可以暂停函数执行,返回任意表达式的值。这种特点使得Generator有多种应用场景。 @@ -661,12 +675,3 @@ function doStuff() { 上面的函数,可以用一模一样的for...of循环处理!两相一比较,就不难看出Generator使得数据或者操作,具备了类似数组的接口。 -## Generator与协程 - -协程(coroutine)是一种程序运行的方式。传统的“子例程”(subroutine)采用堆栈式“后进先出”的执行方式,只有当调用的子函数完全执行完毕,才会结束执行父函数。协程与其不同,多个函数可以并行执行,但是只有一个函数处于正在运行的状态,其他函数都处于暂停态(suspended),函数之间可以交换执行权。也就是说,一个函数执行到一半,可以暂停执行,将执行权交给另一个函数,等到稍后收回执行权的时候,再恢复执行。这种可以并行执行、交换执行权的函数,就称为协程。 - -不难看出,协程适合用于多任务运行的环境。在这个意义上,它与线程很相似,都有自己的执行上下文、可以分享全局变量。它们的不同之处在于,同一时间可以有多个线程处于运行状态,但是运行的协程只能有一个,其他都处于暂停状态。此外,线程是抢先式的,到底哪个线程优先得到资源,必须由运行环境决定,但是协程是合作式的,执行权由协程自己分配。 - -Generator函数是ECMAScript 6对协程的实现,但属于不完全实现,只做到了暂停执行和转移执行权,有一些特性没有实现,比如不支持所调用的函数之中的yield语句。 - -如果将Generator函数看作多任务运行的方式,存在多个进入点和退出点。那么,一方面,并发的多任务可以写成多个Generator函数;另一方面,继发的任务则可以按照发生顺序,写在一个Generator函数之中,然后用一个任务管理函数执行(参考本节的“控制流管理”部分)。