ES6的很多特性都跟Generator扯上关系,而且实际用处比较广, 包含了任何需要异步的模块, 比如ajaxfilesystem, 或者数组对象遍历等都可以用到;

  Generator的使用:

  Generator函数和普通的函数区别有两个, 1:function和函数名之间有一个*号, 2:函数体内部使用了yield表达式;比如这样:

function* gen() {yield "1";yield "2"
}

  这个玩意儿如果运行的话,会返回一个Iterator实例, 然后再执行Iterator实例的next()方法, 那么这个函数才开始真正运行, 并把yield后面的值包装成固定对象并返回,直到运行到函数结尾, 最后再返回undefined

"use strict";
function* fibonacci() {yield 1;yield 2;
}var it = fibonacci();
console.log(it);          // "Generator {  }"
console.log(it.next());   // 1
console.log(it.next());   // 2
console.log(it.next()); //undefined

  

  yield

  Generator函数返回的Iterator运行的过程中,如果碰到了yield, 就会把yield后面的值返回, 此时函数相当于停止了, 下次再执行next()方法的时候, 函数又会从上次退出去的地方重新开始执行;

  如果把yieldreturn一起使用的话, 那么return的值也会作为最后的返回值, 如果return语句后面还有yield, 那么这些yield不生效:

function* gen() {yield 0;yield 1;return 2;yield 3;
};
let g = gen();
console.log(g.next(),g.next(),g.next(),g.next());
//输出:{ value: 0, done: false } { value: 1, done: false } { value: 2, done: true } { value: undefined, done: true }

  我们也不能在非Generator函数中使用yield,比如:

<script>
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {a.forEach(function (item) {if (typeof item !== 'number') {yield* flat(item);} else {yield item;}})
};for (var f of flat(arr)){console.log(f);
}
</script>

  上面的demo因为callback是一个普通函数, 所以编译的时候直接抛出错误提示, 我们需要改成在Generator的函数体中:

<script>
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {var length = a.length;for (var i = 0; i < length; i++) {var item = a[i];if (typeof item !== 'number') {yield* flat(item);} else {yield item;}}
};
for (var f of flat(arr)) {console.log(f);
}
</script>

  或者有个更奇怪的方法,我们把数组的forEach改成Generator函数:

<script>
var arr = [1, [[2, 3], 4], [5, 6]];
Array.prototype.forEach = function* (callback) {for(var i=0; i<this.length; i++) {yield* callback(this[i],i ,this[i]);}
}
var flat = function* (a) {yield* a.forEach(function* (item) {if (typeof item !== 'number') {yield* flat(item);} else {yield item;}})
};for (var f of flat(arr)){console.log(f);
}
</script>

  而且Iterator的return的值不会被for...of循环到 , 也不会被扩展符遍历到, 以下Demo的return 2yield 3完全不生效了, 这个是要注意的;

function* gen() {yield 0;yield 1;return 2;yield 3;
};
let g = gen();
console.log([...g]); //输出:[ 0, 1 ]
for(let foo of g) {console.log( foo ); //输出 0, 1
}

  yield*

  yield*这种语句让我们可以在Generator函数里面再套一个Generator, 当然你要在一个Generator里面调用另外的Generator需要使用: yield* 函数() 这种语法, 都是套路啊:

function* foo() {yield 0;yield 1;
}
function* bar() {yield 'x';yield* foo();yield 'y';
}
for (let v of bar()){console.log(v);
};

  next()方法

  Generator函数返回的Iterator执行next()方法以后, 返回值的结构为:

{value : "value", //value为返回的值done : false //done的值为一个布尔值, 如果Interator未遍历完毕, 他会返回false, 否则返回true;
}

  所以我们可以模拟一个Generator生成器, 利用闭包保存变量, 每一次执行next()方法, 都模拟生成一个{value:value,done:false}的键值对:

function gen(array){var nextIndex = 0;return {next: function(){return nextIndex < array.length ?{value: array[nextIndex++], done: false} :{value: undefined, done: true};}};
};var it = gen(["arr0", "arr1", "arr2", "arr3"]);
console.log( it.next() );
console.log( it.next() );
console.log( it.next() );
console.log( it.next() );
console.log( it.next() ); 

  再浪一点的话,我们也可以模拟一个对象的Iterator, 因为本身对象是没有Iterator的, 我们为对象添加[Symbol.iterator]方法:

<script>
var itObj = {0:"00",1:"11",2:"22",3:"33",length : 4,[Symbol.iterator]() {const _this = this;let index = 0;return {next() {if(index< _this.length) {return {value : _this[index++],done : false}}else{return {value : undefined,done : true}}}}}
};
console.log([...itObj]);
</script>

View Code

  next()方法的参数

  如果给next方法传参数, 那么这个参数将会作为上一次yield语句的返回值 ,这个特性在异步处理中是非常重要的, 因为在执行异步代码以后, 有时候需要上一个异步的结果, 作为下次异步的参数, 如此循环::

<script>
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 }
</script>

  上面的demo看懂了, next()方法的参数怎么使用也就懂了;

  throw方法()

  如果执行Generator生成器的throw()方法, 如果在Iterator执行到的yield语句写在try{}语句块中, 那么这个错误会被内部的try{}catch(){}捕获 :

<script>var g = function* () {try {yield;} catch (e) {console.log('内部捕获0', e);}
};var i = g();
i.next(); //让代码执行到yield处;
try {i.throw('a');
} catch (e) {console.log('外部捕获', e);
}</script>

  如果Interator执行到的yield没有写在try{}语句块中, 那么这个错误会被外部的try{}catch(){}语句块捕获;

<script>
var g = function* () {while(true) {try {yield;} catch (e) {console.log('内部捕获', e);}}
};var i = g();
i.next();try {i.throw('a');i.throw('b');
} catch (e) {console.log('外部捕获', e);
}</script>

  return()方法:

  如果执行Iterator的return()方法, 那么这个迭代器的返回会被强制设置为迭代完毕, 执行return()方法的参数就是这个Iterator的返回值,此时done的状态也为true:

<script>
function* gen() {yield 0;yield 1;yield 2;yield 3;
};
let g = gen();
console.log(g.return("heheda")); //输出:{ value: 'heheda', done: true }
</script.

  Generator中的this和他的原型

  Generator中的this就是谁调用它,那么this就是谁, 我们利用Reflect.apply可以改变Generator的上下文:

function* gen() {console.log(this);yield 0;
};
console.log(gen().next());
console.log(Reflect.apply(gen,"heheda").next());

  Generator生成的Iterator,不但继承了Iterator的原型, 也继承了Generator的原型:

<script>
function* gen() {console.log(this);yield 0;
};
gen.prototype.foo = ()=> {console.log("foo");
}
let g = gen();
console.log(Reflect.getPrototypeOf(g) === gen.prototype); //输出:true
</script>

  所以如果要让生成器继承方法, 我们可以这样, 感觉好酷, 但是Generator内部的this是指向原型的, 也就是说已经把原型污染了:

<script>
function* gen() {this.bar = "bar";yield 0;
};
gen.prototype.foo = ()=> {console.log("foo");
}
let g = Reflect.apply(gen, gen.prototype,[]);
console.log(g.next());  //输出:Object {value: 0, done: false}
console.log(g.bar); //输出:bar
</script>

  实际使用:

  ajax的异步处理, 利用生成器的特性,不但可以用于ajax的异步处理, 也能够用于浏览器的文件系统filesystem的异步:

<html>
<head><meta charset="utf-8"><script src="//cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js"></script>
</head>
<body><script>"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("执行到这儿啦");</script>
</body>
</html>

  以上代码中的console.log("执行到这儿啦");先被执行了, 然后才出现了ajax的返回结果, 也就说明了Generator函数是异步的了;   

  

  利用Generator函数,可以在任意对象上部署iterator接口:

function* iterEntries(obj) {let keys = Object.keys(obj);for (let i=0; i < keys.length; i++) {let key = keys[i];yield [key, obj[key]];}
}let myObj = { foo: 3, bar: 7 };for (let [key, value] of iterEntries(myObj)) {console.log(key, value); //输出:foo 3 , bar 7
}

  参考:

    https://davidwalsh.name/es6-generators
    https://davidwalsh.name/es6-generators-dive
    https://davidwalsh.name/async-generators
    https://davidwalsh.name/concurrent-generators
    http://www.2ality.com/2015/03/es6-generators.html
    http://es6.ruanyifeng.com/#docs/generator
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator

作者: NONO
出处:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830

ES6新特性:Javascript中Generator(生成器)相关推荐

  1. ES6新特性(中)——ES6的集合(set集合、map集合等)

    这里写目录标题 ES6的集合 一.Set集合 1.操作方法 2.遍历方法: 二.WeakSet集合 1.概念理解 2.方法: 3.WeakSet 的应用场景/好处 三.Map集合 1.概念理解 2.属 ...

  2. ES6新特性_ES6中模块暴露数据语法汇总---JavaScript_ECMAScript_ES6-ES11新特性工作笔记043

    上一节说了,在浏览器中使用es6的模块化,来引用js, 上面是之前我们写的这个 m1.js文件 可以看到我们在我们想暴露的,变量或者 函数前面加上了一个 export 关键字 然后我们通过 <s ...

  3. ES6新特性_ES6中Map的介绍与API---JavaScript_ECMAScript_ES6-ES11新特性工作笔记032

    ES6中的map,真的是太灵活了,比起java来说,很灵活了. 可以看到介绍 首先我们创建一个map,名字是m然后 我们添加一个元素,name,尚硅谷,name是key,尚硅谷是value 打印一下右 ...

  4. lsdyna如何设置set中的node_list_如何快速掌握es6+新特性及核心语法?

    国庆刚刚结束,我们开始一波新的学习进程吧. ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.作为前端必备技能,我们来快速开始吧 接 ...

  5. javascript ES6 新特性之 扩展运算符 三个点 ...

    对于 ES6 新特性中的 ... 可以简单的理解为下面一句话就可以了: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中. 作用类似于 Object.assign() ...

  6. 尚硅谷es6新特性笔记

    尚硅谷es6新特性笔记 一.let与const let的变量声明以及声明特性 const的变量声明以及声明特性 const.let.var 三者之间的区别 二.变量解构赋值 三.模板字符串 四.对象的 ...

  7. ES6新特性----面试

    ES6新特性 关键字 let关键字 const关键字 解构赋值 变量的解构赋值 数组的解构赋值 对象的解构赋值 字符串的解构赋值 数值和布尔值的解构赋值 函数参数的解构赋值 用途 模板字符串 箭头函数 ...

  8. 一文快速掌握 es6+新特性及核心语法

    首先先祝各位节日快乐,好好去体验生活的快乐,在假期最后一天里,祝大家收获满满,同时抓住假期的尾巴,收割实用技能. 接下来我会总结一些工作中常用也比较核心的es6+的语法知识,后面又要慢慢开始工作之旅了 ...

  9. ES6新特性之了解ES6以及其发展历史

    ES6 新特性 现在使用主流的前端框架中,如ReactJS.Vue.js.angularjs等,都会使用到ES6的新特性,作为一名高级工程师而言,ES6也就成为了必修课,所以本套课程先以ES6的新特性 ...

  10. es6 获取对象的所有值_前端开发必备 - ES6 新特性之 Set和Map数据结构

    往期回顾: 前端开发必备 - ES6 新特性之 let 和 const 命令 前端开发必备 - ES6 新特性之 变量的解构赋值 前端开发必备 - ES6 新特性之 字符串的拓展 前端开发必备 - E ...

最新文章

  1. Microsoft月度中文速递
  2. [汇编与C语言关系]2. main函数与启动例程
  3. 三大主流开源工作流引擎技术分析与市场预测
  4. 解决Redmi 6 pro底部出现黑边问题
  5. 面试必备:HashMap底层数据结构?jdk1.8算法优化,hash冲突,扩容等问题
  6. python可变对象与不可变对象_python 可变对象与不可变对象
  7. uwsgi03----直接部署
  8. ppt给图片增加高斯模糊_【毕业答辩】PPT美化:如何设计毕业答辩的封面
  9. 2012-12-17 → 2013-01-20 周总结:五周没写周总结了,今天来总结下
  10. MySQL图书借阅系统项目数据库建库表语句(组合主键、外键设置)
  11. 吴恩达深度学习——人脸识别与神经风格转换
  12. ARM一面(二轮技术面)
  13. chrome Axure插件(Mac版)
  14. 射线法判断点在多边形内适用范围_重庆球墨铸铁X射线实时成像实时成像系统真诚合作_丹东华日电气...
  15. mac怎么设置锁屏壁纸,锁屏壁纸和屏幕壁纸不同
  16. kpi绩效考核流程图_XX公司KPI绩效考核案例.doc
  17. 命令行快速切换到指定路径
  18. 李居明 饿火命(3)
  19. ug java环境变量设置_关于UG环境变量
  20. Java公式编辑器开发思路,附项目源码

热门文章

  1. 【VBA编程实例】 如何导出百度云盘的目录
  2. 【Matlab学习笔记】【编程实例】二(将两幅灰度图片调整成相同的尺寸,然后左右拼接到一起)
  3. QQ自动登录 发消息给某人C++/C
  4. ENVI入门系列教程---一、数据预处理---2.1自定义坐标系
  5. 《剑指offer》面试题4——替换空格 C++编程
  6. VS学习笔记(一)创建C++项目
  7. 使用讯飞实现语音听写与语音合成功能
  8. java爬取_java实现爬取知乎用户基本信息
  9. 高性能MySQL笔记——MySQL基础(一)
  10. 项目:聊天室思路(linux下实现,语言:C/C++)