ES6新特性:Javascript中Generator(生成器)
ES6的很多特性都跟Generator扯上关系,而且实际用处比较广, 包含了任何需要异步的模块, 比如ajax, filesystem, 或者数组对象遍历等都可以用到;
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()方法的时候, 函数又会从上次退出去的地方重新开始执行;
如果把yield和return一起使用的话, 那么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 2 和yield 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(生成器)相关推荐
- ES6新特性(中)——ES6的集合(set集合、map集合等)
这里写目录标题 ES6的集合 一.Set集合 1.操作方法 2.遍历方法: 二.WeakSet集合 1.概念理解 2.方法: 3.WeakSet 的应用场景/好处 三.Map集合 1.概念理解 2.属 ...
- ES6新特性_ES6中模块暴露数据语法汇总---JavaScript_ECMAScript_ES6-ES11新特性工作笔记043
上一节说了,在浏览器中使用es6的模块化,来引用js, 上面是之前我们写的这个 m1.js文件 可以看到我们在我们想暴露的,变量或者 函数前面加上了一个 export 关键字 然后我们通过 <s ...
- ES6新特性_ES6中Map的介绍与API---JavaScript_ECMAScript_ES6-ES11新特性工作笔记032
ES6中的map,真的是太灵活了,比起java来说,很灵活了. 可以看到介绍 首先我们创建一个map,名字是m然后 我们添加一个元素,name,尚硅谷,name是key,尚硅谷是value 打印一下右 ...
- lsdyna如何设置set中的node_list_如何快速掌握es6+新特性及核心语法?
国庆刚刚结束,我们开始一波新的学习进程吧. ECMAScript 6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了.作为前端必备技能,我们来快速开始吧 接 ...
- javascript ES6 新特性之 扩展运算符 三个点 ...
对于 ES6 新特性中的 ... 可以简单的理解为下面一句话就可以了: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中. 作用类似于 Object.assign() ...
- 尚硅谷es6新特性笔记
尚硅谷es6新特性笔记 一.let与const let的变量声明以及声明特性 const的变量声明以及声明特性 const.let.var 三者之间的区别 二.变量解构赋值 三.模板字符串 四.对象的 ...
- ES6新特性----面试
ES6新特性 关键字 let关键字 const关键字 解构赋值 变量的解构赋值 数组的解构赋值 对象的解构赋值 字符串的解构赋值 数值和布尔值的解构赋值 函数参数的解构赋值 用途 模板字符串 箭头函数 ...
- 一文快速掌握 es6+新特性及核心语法
首先先祝各位节日快乐,好好去体验生活的快乐,在假期最后一天里,祝大家收获满满,同时抓住假期的尾巴,收割实用技能. 接下来我会总结一些工作中常用也比较核心的es6+的语法知识,后面又要慢慢开始工作之旅了 ...
- ES6新特性之了解ES6以及其发展历史
ES6 新特性 现在使用主流的前端框架中,如ReactJS.Vue.js.angularjs等,都会使用到ES6的新特性,作为一名高级工程师而言,ES6也就成为了必修课,所以本套课程先以ES6的新特性 ...
- es6 获取对象的所有值_前端开发必备 - ES6 新特性之 Set和Map数据结构
往期回顾: 前端开发必备 - ES6 新特性之 let 和 const 命令 前端开发必备 - ES6 新特性之 变量的解构赋值 前端开发必备 - ES6 新特性之 字符串的拓展 前端开发必备 - E ...
最新文章
- Microsoft月度中文速递
- [汇编与C语言关系]2. main函数与启动例程
- 三大主流开源工作流引擎技术分析与市场预测
- 解决Redmi 6 pro底部出现黑边问题
- 面试必备:HashMap底层数据结构?jdk1.8算法优化,hash冲突,扩容等问题
- python可变对象与不可变对象_python 可变对象与不可变对象
- uwsgi03----直接部署
- ppt给图片增加高斯模糊_【毕业答辩】PPT美化:如何设计毕业答辩的封面
- 2012-12-17 → 2013-01-20 周总结:五周没写周总结了,今天来总结下
- MySQL图书借阅系统项目数据库建库表语句(组合主键、外键设置)
- 吴恩达深度学习——人脸识别与神经风格转换
- ARM一面(二轮技术面)
- chrome Axure插件(Mac版)
- 射线法判断点在多边形内适用范围_重庆球墨铸铁X射线实时成像实时成像系统真诚合作_丹东华日电气...
- mac怎么设置锁屏壁纸,锁屏壁纸和屏幕壁纸不同
- kpi绩效考核流程图_XX公司KPI绩效考核案例.doc
- 命令行快速切换到指定路径
- 李居明 饿火命(3)
- ug java环境变量设置_关于UG环境变量
- Java公式编辑器开发思路,附项目源码
热门文章
- 【VBA编程实例】 如何导出百度云盘的目录
- 【Matlab学习笔记】【编程实例】二(将两幅灰度图片调整成相同的尺寸,然后左右拼接到一起)
- QQ自动登录 发消息给某人C++/C
- ENVI入门系列教程---一、数据预处理---2.1自定义坐标系
- 《剑指offer》面试题4——替换空格 C++编程
- VS学习笔记(一)创建C++项目
- 使用讯飞实现语音听写与语音合成功能
- java爬取_java实现爬取知乎用户基本信息
- 高性能MySQL笔记——MySQL基础(一)
- 项目:聊天室思路(linux下实现,语言:C/C++)