Generator “生成器” 简介
Generator 函数是一个函数
但是有两个特征。function
关键字后面有一个星号;函数体内部用到yield
表达式。
function* myGenerator(){
yield 'hi';
yield 'girl';
return 'I Love You';
}
let boy = myGenerator();
boy.next() // { value: 'hi', done: false }
boy.next() // { value: 'girl', done: false }
boy.next() // { value: 'I Love You', done: true }
boy.next() // { value: undefined, done: true }
// 该函数有三种状态 'hi' 'girl' 和结束 'I Love You'
yield “产出” 表达式
就是Genertor
内部用来暂停的关键词,yield
表达式也只能使用在 Generator 函数中。
Genertor
函数的运行逻辑:
(1)当遇到yield
表达式,就会暂停执行后续代码,将紧跟在yield
后面的表达式结果,作为返回对象中的value
属性值。
(2)再一次调用next
方法,会继续向下执行,直到再次遇到yield
表达式。
(3)没有新的yield
表达式时,会运行到函数有return
语句为止,将return
后面的表达式结果,作为返回对象中的value
属性值。
(4)当该函数没return
语句时,则返回的对象的value
属性值为undefined
。
next 方法
yield
表达式本身总是返回undefined
;
next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
function* fn(x){
let a = 3 * (yield (x + 2));
let b = yield (a * 2);
return (a + b);
}
let fn1 = fn(2),fn2 = fn(2);
fn1.next() // {value:4, done:false}
fn1.next() // {value:NaN, done:false}
fn1.next() // {value:NaN, done:true}
fn2.next() // {value:4, done:false}
fn2.next(3) // {value:18, done:false}
fn2.next(6) // {value:15, done:true}
// 注意,由于`next`方法的参数表示上一个`yield`表达式的返回值,所以在第一次使用`next`方法时,传递参数是无效的。
for…of 循环遍历
for...of
循环会自动遍历 Generator 函数,不需要调用next()
方法
function* circle(){
yield 1;
yield 2;
yield 3;
yield 4;
return 'end';
}
for(let el of circle()){
console.log(el);
}
// 1 2 3 4 注意当next方法中返回对象done属性为true,该条对象将不会被返回。
斐波拉契数列
用Generator
实现斐波拉契数列
function* fibonacci() {
let [a, b] = [0, 1];
for (;;) {
yield b;
[a, b] = [b, a + b];
}
}
for (let n of fibonacci()) {
if (n > 1000) break;
console.log(n);
}
实例对象
实现对象具备Iterator
接口
function* objectEntries(){
let keys = Object.keys(this);
for (let key of keys){
yield [key,this[key]];
}
}
let person = {
name: 'shaoin',
age: 88
}
person[Symbol.iterator] = objectEntries;
for(let [key, value] of person){
console.log(key + ":" + value);
}
// name:shaoin age:88
除for...of
循环以外,
function acount(){
yield 1;
yield 2;
return 4;
}
还有:
扩展运算符(...
)
[...acount()] // [1, 2]
Array.from 方法
Array.from(acount()) // [1, 2]
解构赋值
let [x, y] = numbers(); // x = 1, y= 2
Generator.prototype.throw() 抛出错误
throw
方法可以接受一个参数,该参数会被catch
语句接收
function* fn(){
try{
yield;
}catch(e){
console.log(e);
}
};
let o = fn();
o.throw(new Error('出错了')) // Error: 出错了
Generator.prototype.return() 终止函数
return()
方法,返回给定的值,并终结Generator
函数
function* fn(){
yield 1;
yield 2;
}
let o = fn();
o.next() // {value: 1, done: false}
o.return('some') // {value: "some", done: true}
o.next() // {value: undefined, done: true}
next、throw、return 的共同点和差别
它们的作用都可以让Generator
函数继续执行
next()
是将yield
表达式替换成一个已计算的结果或表达式;throw()
是将yield
表达式替换成一个throw
需要执行抛出错误的语句;return()
是将yield
表达式替换成一个return
需要终止和返回值的语句
yield* 表达式使用
yield*
表达式用来在Generator
函数里有效执行另一个Generator
函数。
function* fn1(){
yield 1;
yield 2;
}
function* fn2(){
yield 1;
yield 2;
yield 3;
yield 4;
}
// 相当于
function* fn2(){
yield* fn1();
yield 3;
yield 4;
}
// 相当于
function* fn2(){
for(let e of fn1()){
yield e;
}
yield 3;
yield 4;
}
for (let e of fn2()){
console.log(e)
}
// 1 2 3 4
yield*
后面可以跟一个字符串
或者跟一个数组
。
function* fn(){
yield 'hi';
yield* 'shaoin'
}
let o = fn();
o.next() // {value: 'hi', done: false}
o.next() // {value: 's', done: false}
当Generator
函数作为对象属性时,可以简写
let obj = {
* fn(){
...
}
};
// 相当于
let obj = {
fn:function* (){
...
}
}
协程
“协程”(coroutine),意思是多个线程互相协作,完成异步任务。
协程有点像函数,又有点像线程:
- 第一步,协程C1开始执行;
- 第二步,协程C1执行到一半,进入暂停,执行权转移到协程C2;
- 第三步,(一段时间后)协程C2将执行权交还C1;
- 第四步,协程C1恢复执行。
function* fn(x){
let val = yield x * 2;
return val;
}
var g = fn(2);
o.next() // { value: 4, done: false }
o.next(3) // { value: 3, done: true }
参考文献
阮一峰老师的 ECMAScript 6 入门
求点赞 求评论 求收藏 ~~ 今天的学习记录暂时到这... ~~ 求点赞 评论 求收藏