轻松学会ES6新特性之生成器
生成器虽然是ES6最具魔性的新特性,但也是最难懂得的一节,笔者写了大量的实例来具体化这种抽象的概念,能够让人一看就懂,目的是希望别人不要重复或者减少笔者学习生成器的痛苦经历。
在说具体的ES6生成器之前,我觉得我们应该先来看一个例子,因为它真的很晦涩,我们需要先增加一下兴趣会说话的猫
ES6生成器可以说是ES6新特性中最具魔力的特性,它可以极大的简化代码,并且贯穿了整个ES6的使用。它与我们以前学过的js特性都不同,它可以在函数的内部随时暂停。
那么我们从最简单的定义生成器说起,这可能会相对容易一些,以下是会说话的猫的生成器代码:
function* quips(name) {
yield "hello " + name + "!";
yield "i hope you are enjoying the blog posts";
if (name.toLowerCase().startsWith("x")) {
yield "hey, it's cool how your name starts with an X, " + name;
}
yield "see you later!";
}
它看起来像一个函数,但是也有一些不同
- 它使用function*来声明,而不是function。
- 它使用yield来返回,而不是return。生成器函数可以yield多次而不会退出程序,在执行过程中遇到yield表达式立刻暂停,后续可恢复执行状态,而return直接退出程序。
var meow_iter = quips("Xingu");
//[object Generator]
meow_iter.next();
//{value:"hello Xingu!",done:false}
meow_iter.next();
//{value:"i hope you are enjoying the blog posts",done:false}
meow_iter.next();
//{value:"hey, it's cool how your name starts with an X, Xingu",done:false}
meow_iter.next();
//{value:"see you later!",done:false}
meow_iter.next();
//{value:undefied,done:true}
以上就是会说话的猫生成器的执行过程,看过ES6迭代器的同学可能基本上能看懂这段代码了。但是当你调用一个生成器的时候,它并非立即执行,而是在生成器函数第一句代码执行之前返回一个生成器对象(就是上面的meow_iter),这个时候函数冻结了。当你调用对象的.next()方法的时候,函数调用将其自身解冻并一直运行到下一个yield表达式,再次暂停。 当调用最后一个.next()的时候,返回的done为true,调用结束。这就是基本的生成器执行过程了。
function* num(){
yield 1;
yield 2;
return 3;
yield 4;
}
var itr = num();
console.log(itr.next());
console.log(itr.next());
console.log(itr.next());
console.log(itr.next());
console.log(itr.next());
// Object { value: 1, done: false }
// Object { value: 2, done: false }
// Object { value: 3, done: true }
// Object { value: undefined, done: true }
// Object { value: undefined, done: true }
事实胜于雄辩,当我们在生成器中使用return的时候,那么return的值将会作为最后的返回值,而return后面的yield语句不会生效。
function* num(){
yield 1;
yield 2;
return 3;
yield 4;
}
for(var i of num()){
console.log(i);
}
//1
//2
可以看到for-of并不会遍历到return的值,也不会遍历到return之后yield的值。
function* num(){
console.log("start");
for(var i = 0; i < 3; i++){
yield i;
}
console.log("end");
}
for(var i of num()){
console.log(i);
}
// start
// 0
// 1
// 2
// end
官方对与循环中yield是这样解释的:在循环中,每当生成器执行yields语句,生成器的堆栈结构(本地变量、参数、临时值、生成器内部当前的执行位置)被移出堆栈。然而,生成器对象保留了对这个堆栈结构的引用(备份),所以稍后调用.next()可以重新激活堆栈结构并且继续执行。什么东西?好像很高深的样子。说了这么长一串,无非就是说循环中的yield和正常语句的yield在使用上没有区别。
function* num(){
yield 1;
yield 2;
}
function* num1(){
yield 3;
yield* num();
yield 4
}
for(var i of num1()){
console.log(i);
}
// 3
// 1
// 2
// 4
.next()传参
function* add(num){
var x = yield (num + 1);
console.log(x);
var y = yield (x + 1);
return x + y;
}
var a = add(1);
console.log(a.next());
console.log(a.next());
console.log(a.next());
// Object { value: 2, done: false }
// undefined
// Object { value: NaN, done: false }
// Object { value: NaN, done: true }
这是一个传入参数的例子
function* add(num){
var x = yield (num + 1);
console.log(x);
var y = yield (x + 1);
return x + y;
}
var a = add(1);
console.log(a.next());
console.log(a.next(2));
console.log(a.next(3));
// Object { value: 2, done: false }
// 2
// Object { value: 3, done: false }
// Object { value: 5, done: true }
可见,我们在生成器中yield的返回值不能初始化左边的变量,实际上给.next()方法传参数, 那么这个参数将会作为上一次yield语句的返回值,如上我们传入2作为参数,那么上一个yield(num+1)就相当于返回了2,不然我们无法使用它的返回值,如例1。这样说还是有点难懂,一些复杂的问题往往因为一些简单的举例而变得容易理解。实际上这种给.next()传参的做法常常用来代替异步方式的Ajax请求,看完下面的例子你可能会恍然大悟。
"use strict";
function* main() {
var result = yield request("http://www.filltext.com?rows=10&f={firstName}");
console.log(result);
//do 别的ajax请求;
} function request(url) {
var r = new XMLHttpRequest();
r.open("GET", url, true);
r.onreadystatechange = function () {
if (r.readyState != 4 || r.status != 200) return;
var data = JSON.parse(r.responseText);
//数据成功返回以后, 代码就能够继续往下走了;
it.next(data);
};
r.send();
} var it = main();
it.next();
console.log("执行到这儿啦");
it.next(data)将data作为上一次yield的返回值赋值给result,然后打印result,这种用同步的方式实现了和异步编程一样的效果,真是令人惊讶!
function* producevalues() {
setup();
try {
// ... 生成一些值
} finally {
cleanup();
}
}
for (var value of producevalues()) {
work(value);
}
我们希望无论如何都在cleanup()中释放一些资源,但这里有一些情况:
- 我们没在 try 代码块中调用 work(value) ,如果它抛出异常怎么办?
- 或者假设 for-of 循环包含一条 break 语句或 return 语句。清理步骤又会如何执行呢?
function* num() {
try {
for(var i = 0; i < 3; i++){
yield i;
}
} finally {
console.log("finally run");
}
}
function work(value){
if(value != 1){
console.log(value);
}else{
throw(new Error("error"));
}
}
for(var value of num()) {
work(value);
}
// 0
// finally run
// Error: error
循环中抛出异常,finally照常执行
function* num() {
try {
for(var i = 0; i < 3; i++){
yield i;
}
} finally {
console.log("finally run");
}
}
function work(value){
console.log(value);
}
for(var value of num()) {
if(value == 1){
break;
}
work(value);
}
// 0
// finally run
循环中break或return,finally照常执行。
function* num() {
try {
for(var i = 0; i < 3; i++){
yield i;
}
} finally {
console.log("finally run");
}
}
function work(value){
console.log(value);
}
var a = num();
for(var value of a) {
if(value == 1){
a.return();
}
work(value);
}
// 0
// finally run
// 1
下面我们介绍.throw()方法
function* num(){
try{
yield 1
}catch(e){
console.log("内部捕获")
}
}
var a = num();
a.next();
try{
a.throw("a");
}catch(e){
console.log("外部捕获")
}
//内部捕获
function* num(){
try{
yield 1
}catch(e){
console.log("内部捕获")
}
yield 2
}
var a = num();
a.next();
a.next();
try{
a.throw("a");
}catch(e){
console.log("外部捕获")
}
//外部捕获
最后补充一点关于生成器中的this以及它的原型
- 生成器中的this就是它的调用者。
- 生成器生成的Iterator,不但继承了Iterator的原型, 也继承了Generator的原型。
轻松学会ES6新特性之生成器的更多相关文章
- ES6新特性之生成器函数 (generator function): function*
一.什么是生成器函数(generator function)? 生成器函数是ES6的新特性之一,它是一个在执行时能中途暂时退出,后面重新调用又能重新进入继续执行的一种函数. 并且在函数内定义的变量的所 ...
- ES6新特性三: Generator(生成器)函数详解
本文实例讲述了ES6新特性三: Generator(生成器)函数.分享给大家供大家参考,具体如下: 1. 简介 ① 理解:可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改 ...
- ES6新特性概览
本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...
- Atitit js版本es5 es6新特性
Atitit js版本es5 es6新特性 Es5( es5 其实就是adobe action script的标准化)1 es6新特性1 Es5( es5 其实就是adobe action scrip ...
- ES6新特性简介
ES6新特性简介 环境安装 npm install -g babel npm install -g babel-node //提供基于node的REPL环境 //创建 .babelrc 文件 {&qu ...
- 必须掌握的ES6新特性
ES6(ECMAScript2015)的出现,让前端开发者收到一份惊喜,它简洁的新语法.强大的新特性,带给我们更便捷和顺畅的编码体验,赞! 以下是ES6排名前十的最佳特性列表(排名不分先后): 1.D ...
- 你不知道的JavaScript--Item24 ES6新特性概览
ES6新特性概览 本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代 ...
- 34.js----JS 开发者必须知道的十个 ES6 新特性
JS 开发者必须知道的十个 ES6 新特性 这是为忙碌的开发者准备的ES6中最棒的十个特性(无特定顺序): 默认参数 模版表达式 多行字符串 拆包表达式 改进的对象表达式 箭头函数 =&> ...
- ES6新特性概览1
本文基于lukehoban/es6features ,同时参考了大量博客资料,具体见文末引用. ES6(ECMAScript 6)是即将到来的新版本JavaScript语言的标准,代号harmony( ...
随机推荐
- python装饰器练习题
练习题1. 请使用python, 对下面的函数进行处理, def hello(name): print "hello, %s" % name 在函数被调用时打印耗时详情 <f ...
- flex布局常用属性
最近喜欢flex布局,它可以完美的实现响应式布局,下边我总结了它的一些常用属性,喜欢的,也可以练习写一下,很好用~~~ 注意:使用了flex布局,对于子元素的float.clear和vertical- ...
- Excel的实用函数
在介绍Excel函数前先说明两个概念:公式和函数. 公式:由用户自行设计对工作表进行计算和处理的计算式,以等号"="开始,其内部可以包括函数.引用.运算符和常量. 函数:即是预先定 ...
- Java线程池详解
一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...
- NOIP2017SummerTraining0705
个人感受:这一场考试是网开着的,然后第一题就水过了,第二三题应该是暴力吧,然后各水了50.拿了200分.排名第10. 问题 A: 重复字符串 时间限制: 1 Sec 内存限制: 256 MB提交: ...
- MyEclipse的JQuery.min.js报错红叉解决办法
MyEclipse的JQuery.min.js报错红叉解决办法 1.选中报错的jquery文件"jquery-1.2.6.min.js".2.右键选择 MyEclipse--> ...
- ServiceStack.Text / Newtonsoft.Json 两种json序列化性能比较
JSON序列化现在应用非常多,尤其在前后端分离的情况下,平常大多数C#下都使用Newtonsoft.Json来操作,量少的情况下,还可以忽略,但量大的情况下就要考虑使用ServiceStack.Tex ...
- SQL中游标的用法
游标:是用来对表从上下每行循环取值,将值连接成为字符串.例子:对 pubs 数据库的dbo.titles 表.1.取得表中的总价格:select sum(price) from dbo.titles2 ...
- Angular - 预加载 Angular 模块
Angular - 预加载延迟模块 在使用路由延迟加载中,我们介绍了如何使用模块来拆分应用,在访问到这个模块的时候, Angular 加载这个模块.但这需要一点时间.在用户第一次点击的时候,会有一点延 ...
- Python自学笔记-列表生成式(来自廖雪峰的官网Python3)
感觉廖雪峰的官网http://www.liaoxuefeng.com/里面的教程不错,所以学习一下,把需要复习的摘抄一下. 以下内容主要为了自己复习用,详细内容请登录廖雪峰的官网查看. 列表生成式 列 ...