ES6笔记
来源:互联网 发布:市场数据分析报告 编辑:程序博客网 时间:2024/06/05 09:44
Generator 是一个分步执行的函数,可以根据需要执行下一步。合理利用这个特性可以很好解决异步编程回调嵌套的问题。
简介
Generator很像是一个函数,但是你可以暂停它的执行。你可以向它请求一个值,于是它为你提供了一个值,但是余下的函数不会自动向下执行直到你再次向它请求一个值。
特征
function
后面跟*
,函数体内有yield
关键字- 执行Genertor函数返回一个遍历器对象,遇到
yield
暂停执行,调用next
继续执行 next
方法返回的对象中value
表示当前指针对应的yield语句的返回值,done
表示遍历是否结束- 当遍历结束之后,重复调用都只会返回
{value: undefined, done: true}
- 如果函数有
return
,则返回return
后面表达式的值做为value
的值,如果没有,则返回undefined
做为value
的值
function* fn1(){yield ‘a’yield ‘b’return ‘c’;}var g1 = fn1();g1.next();//{value: “a”, done: false}g1.next();//{value: “b”, done: false}g1.next();//{value: “c”, done: false}g1.next();//{value: undefined, done: true}
多维数组扁平化
var arr = [1, [[2, 3], 4], [5, 6]];var flat = function* (a){var len = a.length, item;for(var i=0; i<len; i++){item = a[i];if(typeof item !== 'number'){yield* flat(item);}else{yield item;}}}var g2 = flat(arr);g2.next()//{value: 1, done: false}for(var f of g2){console.log(f)}// 2,3,4,5,6
Iterator接口
任意一个对象的 Symbol.iterator
方法,等于该对象的遍历器生成函数
将 Generator
函数赋值给对象的 Symbol.iterator
属性,可使对象具有Iterator接口
具有Iterator接口的对象可使用 ...
延展符展开
var k = {};k[Symbol.iterator] = function* (){yield 1;yield 2;}[...k] //[1, 2]
Generator
函数返回的遍历器对象也有Symbol.iterator
对象,执行后返回自身
var m = function* (){}var g3 = m();g3[Symbol.iterator]() == g3 //true
yield
语句本身没有返回值,或者永远返回 undefined
next
方法有参数时,参数将会被当然上一个 yield
语句的返回值
function* f() {for(var i=0; true; i++) {//这里,如果next没有传参数过来,reset将一直是undefinedvar reset = yield i;console.log(typeof reset)if(reset) { i = -1; }}}var g = f();g.next(); //{value: 0, done: false}g.next(); //undefined {value: 1, done: false}g.next(true); //boolean {value: 0, done: false}
Generator函数在运行时,内部的 上下文(context )是保持不变的,但可以通过 next
方法不但的向函数体注入参数,从而改变函数的行为。next
方法第一次调用时的参数会被忽略,只有后面的传参会被使用,第一个next方法可视作是启动遍历器对象
function* foo(x) {var y = 2 * (yield (x + 1));var z = yield (y / 3);return (x + y + z);}//这里,由于yield本身的返回值是undefined,所以导致后面的计算都为NaNvar a = foo(5);a.next() // Object{value:6, done:false}a.next() // Object{value:NaN, done:false}a.next() // Object{value:NaN, done:true}//这里,第二次的next传递了参数12,y=24, z=24/3=>8//第三次 x=5 z=13 y=24 故x+y+z = 42var b = foo(5);b.next() // { value:6, done:false }b.next(12) // { value:8, done:false }b.next(13) // { value:42, done:true }
for...of
遍历
可以使用 for...of
遍历Generator
对象,不需要调用 next
function* fn(){yield 1;yield 2;yield 3;return 4;}for(var i of fn()){console.log(i)}//1,2,3
由于 for...of
遍历时,当返回的对象 done
为 true
时就终止了循环,且不返回对象的 value
, 所以最后的 4 没有输出,执行到 return
返回的是 {value: 4, done: true}
例1:利用Generator函数和for…of循环,实现斐波那契数列
function* fibonacci(){let [prev, curr] = [0, 1];for(;;){[prev, curr] = [curr, prev+curr];yield curr;}}for(var y of fibonacci()){if(y>50) break;console.log(y);}// 1, 2, 3, 5, 8, 13, 21, 24
例2:给原生对象添加Iterator遍历接口
function* objectEntries(){let keys = Object.keys(this);for( let key of keys){yield [key, this[key]]}}var obj = {name: 'jack', age: 12, city:'hangzhou'}obj[Symbol.iterator] = objectEntries;for(let [key, value] of obj){console.log(`${key}: ${value}`)}//name: jack//age: 12//city: hangzhou
for...of
循环 / 扩展运算符...
/ 解构赋值 / Array.from
都可以操作Generator函数返回的Iterator对象
function* numbers(){yield 1;yield 2;return 3;yield 4;}[...numbers()]//[1, 2]Array.from(numbers())//[1, 2]let [x, y] = numbers()//[1, 2]for(let n of numbers()){console.log(n)}//1, 2
Generator.prototype.throw
Generator函数内部部署 try...catch
可以对多个 yield 语句进行错误捕捉
Generator函数内部抛出错误可以被函数体外catch捕获
Generator函数体外抛出错误可以被函数体内catch捕获
function* fnA(){let x = 1;try{yield console.log('a')yield console.log('b')//这里报错会中断后面的执行,返回{value: undefined, done: true}yield x.toUppercase()yield console.log('d')}catch(e){console.log('内:', e)throw e}}var ga = fnA()try{ga.next();//外面的错误会被内部catch捕获ga.throw('在函数体外部抛出的错误')}catch(e){console.log('外:', e)}//由于内部中断了执行,再调用next只会拿到{value: undefined, done: true}的结果ga.next()//输出结果://a//内: 在函数体外部抛出的错误//外: 在函数体外部抛出的错误//{value: undefined, done: true}
如果Generator函数返回的Iterator对象在外部抛出了错误,而函数体内部又没有捕获,那么这个错误将抛给全局,程序中断
var gen = function* gen(){yield console.log('hello');yield console.log('world');}var g = gen();g.next();g.throw();// hello// Uncaught undefined 报错
Generator函数返回的Iterator对象throw一个错误后,会同时调用一次next方法
var gen = function* gen(){try {yield console.log('a');} catch (e) {// ...}yield console.log('b');yield console.log('c');}var g = gen();g.next() // ag.throw() // bg.next() // c
Generator.prototype.return
遍历器对象调用return可以改变返回的结果的vaule,并结束遍历返回的done为true
如果Generator函数内部有try…finally代码块,那么return方法会推迟到finally代码块执行完再执行
function* numbers () {yield 1;try {yield 2;yield 3;} finally {yield 4;yield 5;}yield 6;}var g = numbers()g.next() // { done: false, value: 1 }g.next() // { done: false, value: 2 }g.return(7) // { done: false, value: 4 }g.next() // { done: false, value: 5 }g.next() // { done: true, value: 7 }
yield*
用于在一个Generator函数里面执行另一个Generator函数
function* fn1(){yield 4;yield 5;}function* fn2(){yield 1;yield 2;yield 3;yield* fn1();yield 6;}function* fn3(){yield 1;yield 2;yield fn1();yield 6;}[...fn2()] //[1, 2, 3, 4, 5, 6] 将fn1中的内容展开了[...fn3()] //[1, 2, fn1, 6] fn1为Generator返回的iterator对象
yield* 在Generator函数中可以遍历任何具有Iterator接口的对象
function* fn4(){yield* [1,3,4]yield* 'Hllo'}[...fn4()]//[1, 3, 4, "H", "l", "l", "o"]
yield 后面的Generator函数中可以通过 return 返回数据给 yield 所在的Generator函数
function* fn5(){yield 1;yield 2;return 3;}function* fn6(){yield 4;var res = yield* fn5();yield res;yield 5;}[...fn6()] //[4, 1, 2, 3, 5]
多维数组转一维数组
function* flatArr(arr){if(Array.isArray(arr)){for(var k = 0; k<arr.length; k++){yield* flatArr(arr[k])}}else{yield arr;}}var arr1 = [1,[2,3,[4,5,6]],7,[8,9]]for(var i of flatArr(arr1)){console.log(i)}// 1,2,3,4,5,6,7,8,9
二叉树遍历
function Tree(left, label, right){this.left = left;this.label = label;this.right = right;}function make(arr){if(arr.length === 1) return new Tree(null, arr[0], null);return new Tree(make(arr[0]), arr[1], make(arr[2]))}function* inorder(t){if(t){yield* inorder(t.left)yield t.labelyield* inorder(t.right)}}var tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);var res = [];for(var k in inorder(tree)){res.push(k)}console.log(res)//["a", "b", "c", "d", "e", "f", "g"]
做为对象属性
var obj = {*fn(){yield 1;yield 2;}}//等同于var obj2 = {fn: function* (){yield 1;yield 2;}}[...obj.fn()]//[1, 2][...obj2.fn()]//[1, 2]
状态保持
function* tick(){while(true){yield 'Tick'yield 'Tock'}}var t = tick();t.next();t.next();t.next();t.next();
应用场景
异步操作同步化
使用嵌套回调
function asyncFn1(done){setTimeout(()=>{console.log(1);done(1);}, 1500);}function asyncFn2(done){setTimeout(()=>{console.log(2);done(2);}, 2000);}function asyncFn3(done){setTimeout(()=>{console.log(3);done();}, 2500);}asyncFn1(function(args1){asyncFn2(function(args2){asyncFn3(function(args3){//...})})})
使用Generator封装,可以简化回调,让异步写起来更像是同步
//使用done回调保证任务按顺序执行function asyncFn1(done){setTimeout(()=>done(1), 1500);}function asyncFn2(done){setTimeout(()=>done(2), 2000);}function asyncFn3(done){setTimeout(()=>done(3), 2500);}//co的简单实现function co(task){var gen = task();next();function next(res){var ret;ret = gen.next(res);if(ret.done) return;if(typeof ret.value === 'function'){ret.value(function(){//依次遍历执行,如果没遍历完成,则递归调用next.apply(this, arguments)})return;}}}co(function* task(){try{var res1 = yield asyncFn1;//第1次 next fn1返回结果1console.log(res1) //第2次 next 传递fn1的结果 1var res2 = yield asyncFn2;console.log(res2) //第3次 next 传递fn2的结果 2var res3 = yield asyncFn3;console.log(res3) //第4次 next 传递fn3的结果 3}catch(e){// ...}})// 1, 2, 3
任务流程管理
依次执行数组中的步骤
var step1 = () => 1;var step2 = () => 2;var step3 = () => 3;var steps = [ step1, step2, step3 ]function* iterateSteps(steps){for(var step of steps){yield step()}}[...iterateSteps(steps)] // [1, 2, 4]
将多个步骤组合成多个任务,并依次执行这多个任务
var step1 = () => 1;var step2 = () => 2;var step3 = () => 3;var step4 = () => 4;var step5 = () => 5;var step6 = () => 6;var step7 = () => 7;var step8 = () => 8;var job1 = [ step1, step2, step3 ]var job2 = [ step4, step5, step6 ]var job3 = [ step7, step8 ]var jobs = [job1, job2, job3]function* iterateSteps(steps){for(var step of steps){yield step()}}function* iterateJobs(jobs){for(var job of jobs){yield* iterateSteps(job)}}[...iterateJobs(jobs)] //[1, 2, 3, 4, 5, 6, 7, 8]
部署Iterator接口
给任意对象部署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);}
作为类数据结构
function doStuff(){return [1, 2, 3]}for(var i of doStuff()){console.log(i)}// 1, 2, 3function* genStuff(){yield 1;yield 2;yield 3;}for(var i of genStuff()){console.log(i)}// 1, 2, 3
- ES6笔记
- ES6笔记
- ES6笔记
- ES6笔记
- ES6笔记
- ES6笔记
- ES6笔记
- es6 笔记
- Es6笔记
- Es6笔记
- ES6笔记
- es6开发:ES6学习笔记
- ES6学习笔记
- ES6 笔记 0
- ES6学习笔记
- ES6笔记(一)
- ES6学习笔记
- ES6学习笔记
- 深入理解 Promise (下)
- UIDevice 获取设备相关信息备忘
- 解决WAP端用Dreamweaver画热点偏移现象
- ES6笔记
- VC调用Matlab生成的c
- ES6笔记
- wxpython实现简单图书管理系统
- ZJCOJ 朋友Z与方程(二分求解模板题)
- java创建对象:new和newInstance的不同
- 离屏渲染优化详解:实例示范+性能测试
- 使用MyBatis Generator自动创建代码
- 深入掌握 ECMAScript 6 异步编程(四):async函数的含义与用法
- FZU1475 (map)之 不同的单词
- 微服务实践(七):从单体式架构迁移到微服务架构