ES6+ Generator 基础
1. 前言
前面我们花了三节深入地学习了 ES6 的异步 解决方 案 Promise,本节学习的 生成 器也是为了 解决 异步而生的,但是它的出发思路和 Promise 截然不同。
上节我们学习了 ES6 中 迭代 的相关 内容 ,并实现了 一个 迭代器。我们知道实现 一个 迭代器,我们需要手动 添加 对象的 Symbol.i tera tor 属性 ,并需要实现 next 方法 。那么有没有什么可以帮助我们 自动 实现迭代器呢?ES6 给出了 生成 器的 方法 来满足我们的需求。我们不需要在对象上 添加 Symbol.i tera tor 属性 ,使用 生成 器 函数 就可以实现迭代器的 功能 。本节我们将学习 生成 器的相关概念和基础 用法 。
生成 器是 一个 灵活的结构,能使得 一个 函数 块内部暂停和恢复 代码 执行的能力。在实际应用中,使用 生成 器可以 自定义 迭代器和协程。
2. 生成 器对象和 生成 器 函数
有些概念是我们必须要理解的,前面在学习迭代器的时候,我们学习了迭代协议和迭代器协议,实现 一个 迭代器需要满足这两个协议才算是 一个 真正的迭代器。而本节的 生成 器和 生成 器 函数 也是如此,我们也需要知道 生成 器对象和 生成 器 函数 概念和它们直接的关系。
Generator 就是我们说的 生成 器,它包含两个概念 生成 器对象和 生成 器 函数 。首先,要理解的是 生成 器对象和迭代器的关系, 生成 器对象是遵守迭代协议和迭代器协议实现的 I tera ble 接口,可以理解 生成 器对象其实也是 一个 迭代器;然后,我们需要理解什么是 生成 器 函数 , 生成 器 函数 是由 function * 来定义的,并且返回结果是 一个 Generator 对象。
生成 器是 一个 特殊的 函数 ,在 调用 后会返回 一个 生成 器对象,这个 生成 器对象是遵守可迭代协议和迭代器协议实现的 I tera ble 接口。 生成 器可以使用 yield 关键字来暂停执行的 生成 器 函数 :
function * generator ( ) { yield 'a' ; yield 'b' ; } var gen = generator ( ) ; // Object [Generator] {}
2.1 Generator.prototype.next()
生成 器的 next () 方法 和迭代器返回的结果是一样的,返回了 一个 包含 属性 done 和 value 的对象,该 方法 也可以通过接受 一个 参数用以向 生成 器传值。
使用 yield 返回的值会被迭代器的 next () 方法 捕获:
var gen = generator ( ) ; gen . next ( ) // {value: 'a', done: false} gen . next ( ) // {value: 'b', done: false} gen . next ( ) // {value: undefined, done: true}
从上面 代码 的执行结果可以看出, 生成 器 函数 在执行后会返回 一个 生成 器对象,这个 生成 器对象满足迭代协议和迭代器协议,所以我们可以去手动 调用 它的 next () 方法 去 获取 每一步的返回值。从这里可以看出, 生成 器其实就是迭代器的 一个 应用,并且这个应用会在异步中大放异彩。
2.2 Generator.prototype.return()
return() 方法 返回给定的值并结束 生成 器。
var gen = generator ( ) ; gen . next ( ) ; // { value: 'a', done: false } gen . return ( "imooc" ) ; // { value: "imooc", done: true } gen . next ( ) ; // { value: undefined, done: true }
另外,如果对已经完成状态的 生成 器 调用 return(value) 则 生成 器会一直保持在完成状态,如果出入参数, value 会设置成传入的参数, done 的值不变:
var gen = generator ( ) ; gen . next ( ) ; // { value: 1, done: false } gen . next ( ) ; // { value: 2, done: false } gen . next ( ) ; // { value: undefined, done: true } gen . return ( ) ; // { value: undefined, done: true } gen . return ( ) ; // { value: 1, done: true }
2.2 Generator.prototype.throw()
throw() 方法 用来向 生成 器抛出异常,并恢复 生成 器的执行,返回带有 done 及 value 两个 属性 的对象。
function * generator ( ) { while ( true ) { try { yield 'imooc' } catch ( e ) { console . log ( "Error caught!" ) ; } } } var gen = generator ( ) ; gen . next ( ) ; // { value: "imooc", done: false } gen . throw ( new @H_768_ 419 @Error ( "error" ) ) ; // "Error caught!"
3. Generator 案例
3.1 类数组转化
将 一个 类数组转化为 一个 真正的数组方式有很多,ES6 提供了 Array.from() 可以将类数组转化为数组 。另外在一些 函数 中可以使用 [...argument] 的方式转化类数组。
function fn ( ) { const arg = [ ... arguments ] ; console . log ( arg ) ; } fn ( , , ) ; // [1, 2, 3]
当然我们知道类数组的定义,所以我们自己定义 一个 类数组,看能不能使用展开运算符将类数组转化为数组:
const likeArr = { : , : , length : , } console . log ( [ ... likeArr ] ) ; // Uncaught TypeError: likeArr is not i tera ble
上面 代码 中我们定义了 一个 类数组,但是使用展开运算符报错了, 提示 我们 likeArr 不是 一个 迭代器。因为在 函数 中类数组是内部帮我们实现了迭代器的 功能 ,而我们自己定义的类数组是不具有迭代器 功能 的,那我们来自己实现 一个 :
likeArr [ Symbol . i tera tor ] = function ( ) { let index = ; return { next : ( ) => { return { value : this [ index ] , done : index ++ === this . length } } } } console . log ( [ ... likeArr ] ) ; // [1, 2]
上面的 代码 我们在 likeArr 对象上定义了 Symbol.i tera tor 它具有迭代 功能 。上面 代码 中我们需要手动地去实现 next () 方法 ,这比较麻烦,那能不能简化一下呢?我们的 生成 器 函数 就出场了:
likeArr [ Symbol . i tera tor ] = function * ( ) { let index = ; while ( index !== this . length ) { yield this [ index ++ ] ; } } console . log ( [ ... likeArr ] ) ; // [1, 2]
上面的 代码 使用了 生成 器 函数 ,并且没有去手动实现 next () 方法 ,从这里我们也能很清楚地知道迭代器和 生成 器的关系。而且使用 生成 器 函数 更加简单方便。
3.2 单步 获取 质数
还有 一个 案例是面试中经常会考到的:
题目:实现 一个 函数 ,每次 调用返回 下 一个 质数,要求不使用 全局变量 ,且 函数 本身不接受任何参数
从题目的要求可以知道,这个 函数 每次 调用 都会返回 一个 质数,也就是说每次 调用 后都会返回 一个 函数 。
首先我们定义 一个 判断 一个 数是否为质数的 方法 :
function isPrime ( num ) { for ( let i = ; i <= Math . sqrt ( num ) ; i ++ ) { if ( num % i === ) { return false } } return true }
传统的方式是使用闭包 方法 来 解决 :
function primeHandler ( ) { let prime = return ( ) => { while ( true ) { prime ++ if ( isPrime ( prime ) ) { return prime } } } } const getPrime = primeHandler ( ) console . log ( getPrime ( ) ) ; // 2 console . log ( getPrime ( ) ) ; // 3 console . log ( getPrime ( ) ) ; // 5
既然是单步执行的,那么我们就可以使用迭代器方式实现:
var prime = { } prime [ Symbol . i tera tor ] = function ( ) { let prime = ; return { next ( ) { while ( true ) { prime ++ if ( isPrime ( prime ) ) { return prime ; } } } } } var getPrime = prime [ Symbol . i tera tor ] ( ) . next ; console . log ( getPrime ( ) ) ; // 2 console . log ( getPrime ( ) ) ; // 3
上 一个 实例我们知道实现迭代器的方式是很麻烦的,可以使用 生成 器 函数 去替代迭代器的 功能 ,所以上面的 代码 可以使用 生成 器 函数 改造如下:
function * primeGenerator ( ) { let prime = while ( true ) { prime ++ if ( isPrime ( prime ) ) { yield prime } } } var getPrime = primeGenerator ( ) . next ( ) . value console . log ( getPrime ( ) ) ; // 2 console . log ( getPrime ( ) ) ; // 3
4. 小结
本节我们主要学习了 生成 器的概念和 用法 ,需要 生成 器对象是由 生成 器 函数 返回的结果, 生成 器对象是遵守迭代协议和迭代器协议实现的 I tera ble 接口。 生成 器其实就是对迭代器的应用。另外,通过两个案例更加深刻地理解了 生成 器的应用场景,对比了 生成 器和迭代器的不同。
查看更多关于ES6+ Generator 基础的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did91805