混子前端所知道关于ES6的Generator
栏目: JavaScript · 发布时间: 6年前
内容简介:前面文章介绍了Iterator,本文继续介绍Generator。Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。---本文摘选阮一峰《ECMAScript 6 标准入门》
前面文章介绍了Iterator,本文继续介绍Generator。
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
---本文摘选阮一峰《ECMAScript 6 标准入门》
基本概念
Generator函数从语法上是一个状态机,封装了多个内部状态,执行Generator函数会返回一个遍历器对象,返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。
Generator函数从形式上是一个普通函数,但是有两个特征:
- function 关键字与函数名之间有一个星号
- 函数体内部使用 yield 语句,定义不同的内部状态
JS code:
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next() // { value: 'hello', done: false }
hw.next() // { value: 'world', done: false }
hw.next() // { value: 'ending', done: true }
hw.next() // { value: undefined, done: true }
复制代码
说明: Generator函数 helloWorldGenerator有三个状态:hello,world 和 return 语句,这里调用 Generator 函数后并不执行,而是返回一个指向内部状态的指针对象,开发人员必须手动调用遍历器对象的next方法,使指针移动到下一个状态,即 Generator 函数是分段执行的,yield语 句是暂停执行的标记,而 n ext 方法可以恢复执行。
next 方法返回一个对象,对象value是当前 yield 语句的值,done表示遍历的状态 ( false: 没结束 )
yield 语句
Generator函数返回的遍历器对象, 只有调用 next 方法才会遍历下一个内部状态,所以提供了 yield 暂停执行函数。
遍历器对象的 next 方法运行逻辑如下:
- 遇到 yield 语句暂停执行后面的操作,并将在 yield 后面那个表达式的值,作为返回对象的 value 属性值
- 下次调用 next 方法时,再继续往下执行,直到遇到下一个 yield 语句
- 如果没有遇到新的 yield 语句,就一直运行函数结束 到 return 语句为止,将 return 的值作为返回的对象 value 的属性值
- 如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined
yield语句与return的区别:
- return 语句不具备位置记忆功能,即函数遇到 yield 函数暂停执行,下一次在从该位置继续执行。
- 一个函数里只能执行一次 return 语句,但可以执行多次 yield 语句
Generator函数可以不用 yield 语句,这时就变成了一个单纯的暂缓执行函数,来看代码:
JS code:
function* f() {
console.log('执行了!')
}
var generator = f();
setTimeout(function () {
generator.next()
}, 2000);复制代码
注意: 以上代码直接调用函数f(); 不会执行console语句,不管有没有 yield 语句都要执行 next 方法才能调用 Generator 函数,另外 yield 函数也不能存在普通函数中,否则会直接抛出错误,还需要注意的是在 Generator 函数的 forEach / map 等循环语句中,也不可以调用 yield 方法,否则会产生语法错误,另外,yield 语句如果在表达式中,必须用括号扩起来。
与Iterator接口的关系
任意一个对象的 Symbol.iterator 方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。但 由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的 Symbol.iterator 属性,从而使得该对象具有 Iterator 接口,来看代码:
JS code:
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1,2,3]
复制代码
说明: 上面代码 Generator函数赋值给 Symbol.iterator 属性,因此 myIterable 对象具有了Iterator接口,可以被 ... 运算符遍历了。
next方法的参数
next 方法可以带一个参数,该参数会作为上一个 yield 语句的返回值,来看代码:
JS code:
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
复制代码
说明:
第一个 a.next() 没有任何争议; 第二个 a.next() 为什么抛出NaN,因为next 没有携带参数默认 undefined,导致 undefined / 3 后变成NaN;同理第三个 a.next() 中return(x+y+z),内部运作其实是 5 + NaN + undefined,所以 value 也是 NaN;
下面来看一下第二次 b.next() value 输出8,是传递了12作为上一次yeild的值,y就变成了2*12=24,那24/3=8,所以value 为 8;来看一下第三次b.next(),此时 x 为 5 毫无争议,z是传递进去的13,由于上一次next传入的值为12,y=12*2=24,所以return(5+24+13)输出42;
注意: next 方法的参数表示上一个 yield 语句的返回值,所以第一次使用 next 不能带有参数
Generator.prototype.throw()
Generator函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出错误,然后在Generator函数体内捕获,来看代码:
JS code:
var g = function* () {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b
复制代码
说明: 上面代码遍历器 i 连续抛出两个错误,第一个错误被 Generator 函数题内 catch 捕获,i 的第二个错误由于 Generator 函数内部的
catch 语句已经执行过了,不会再捕捉到这个错误了, 所以这个错误被抛出 Generator 函数体,被函数体外的 catch语句捕获。
Generator.prototype.return()
Generator函数返回的遍历器对象 return 方法,可以返回给定的值,终结遍历Generator函数。
JS code:
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
复制代码
说明: 遍历器对象 g 调用 return 方法后,返回值 value 属性就是 return 方法的参数 foo,同时, Generator函数的遍历就终止了,done 值置为true。
yield*语句
如果在Generater函数内部,调用另一个Generator函数,默认情况下是没有效果的,来看代码:
JS code:
function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
foo();
yield 'y';
}
function* gen() {
yield 'x';
yield* foo();
yield 'y'
}
for (let v of bar()){
console.log(v);
}// 'x'
// 'y'
for (let v of gen()){
console.log(v);
}
// 'x'
// 'a'
// 'b'
// 'y'复制代码
说明: 上面代码 在 bar 里面调用 foo ,是不会有效果的,要 用到 yield* 语句,用来在一个Generator 函数里面执行另一个 Generator 函数。
Generator函数的this
对象上的方法。
但如果把 Generator 函数 当作普通的构造函数,并不会生效,同时 Generator 函数也不能跟 new 命令一起用,来看代码:JS code:
function* gen() {
this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
function F() {
return gen.call(gen.prototype);
}
var f = new F();
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
f.a // 1
f.b // 2
f.c // 3
复制代码
说明: 可以更换执行者 F 内部的 this 对象绑定 对象,然后调用它,返回一个Iterator对象
那文章的最后还是老规矩,欢迎大家点赞和纠错。
祝各位周一愉快!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 混子前端带你入门webpack
- 混子前端所知道关于ES6的Promise
- 混子前端所知道关于ES6的Iterator
- 混子前端在开发公众号嵌套webview遇到的常见问题
- 前端科普系列(三):CommonJS 不是前端却革命了前端
- 前端科普系列(三):CommonJS 不是前端却革命了前端
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP 5完全攻略
杜江 / 2010-5 / 79.00元
《PHP 5完全攻略(畅销书升级版)》是目前第一本真正介绍PHP 5及MySQL 5新增语法与功能的中文版本权威宝典!《PHP 5完全攻略(畅销书升级版)》本着精、全、要三宗旨,从理论中延伸,从实践中深入,翔实并完善地描述了PHP 5的开发特性与MySQL 5数据库。《PHP 5完全攻略(畅销书升级版)》分为两大部分,第1部分主要阐述PHP开发的基础知识,如PHP数组与表单处理、PHP 5面向对象......一起来看看 《PHP 5完全攻略》 这本书的介绍吧!