混子前端所知道关于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 不是前端却革命了前端
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Data Structures and Algorithm Analysis in Java
Mark A. Weiss / Pearson / 2006-3-3 / USD 143.00
As the speed and power of computers increases, so does the need for effective programming and algorithm analysis. By approaching these skills in tandem, Mark Allen Weiss teaches readers to develop wel......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!