ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。
1. let 和 const
1 { 2 var a=5; 3 let b=10; 4 } 5 console.log(a); 6 console.log(b);
控制台就是这样输出:
也就是说,var声明的变量由于不存在块级作用域所以可以在全局环境中调用,而let声明的变量由于存在块级作用域所以不能在全局环境中调用。
再来看一个经典例子(闭包):
1 var a=[]; 2 //执行for循环 3 for(var i=0;i<10;i++){ 4 a[i]=function(){ //因为这个是定义,并没有调用方法,不会执行 5 console.log(i); 6 }; 7 } 8 //for循环之后,此时 i = 10;再次执行a[6]();因为 i 一直被引用,所以不会回收,进入到 a[i] 的方法里面, 打印的是 i ,也就是10 9 a[6](); //输出10
如果使用 let
1 var a=[]; 2 for(let i=0;i<10;i++){ 3 a[i]=function(){ 4 console.log(i); 5 }; 6 } 7 a[6](); //打印6
a[6]函数(闭包)这个执行环境中,它会首先寻找该执行环境中是否存在 i,没有找到,因为 i 是块级作用域,就沿着作用域链继续向上到了其所在的代码块执行环境,找到了i=6,于是输出了6,即a[6]();的结果为6。这时,闭包被调用,所以整个代码块中的变量i和函数a[6]()被销毁。
const 是定义常量:const a = 14; 此后变量 a 的值无法更改覆盖。
2. 字符串拼接
1 //传统字符串拼接 2 var s1 = ‘生物膜系统组装又拆分,变幻莫测;‘; 3 var s2 = ‘你的好多细胞在分裂,‘; 4 var str = ‘孩子们:请听我说!‘+s2+‘有丝,减数,哪管白天和黑夜。‘+ 5 ‘染色体,细胞核时隐时现,‘+s1+‘核糖体在mRNA上穿梭忙碌,‘+‘几千种酶各司其职,将活化能狠狠打折。‘; 6 console.log(str); 7 8 // 字符模板的写法 9 var s1 = ‘染色体,细胞核时隐时现,‘; 10 var s2 = ‘你的好多细胞在分裂,‘; 11 var str = `孩子们:请听我说!${s2}有丝,减数,哪管白天和黑夜。${s1}生物膜系统组装又拆分,变幻莫测;核糖体在mRNA上穿梭忙碌,几千种酶各司其职,将活化能狠狠打折。`; 12 console.log(str);
es6使用 ` ` 包裹字符串,即使断开,也可以用这个符号包裹起来合并成一个字符串。
3. 解构赋值
1 // 以前我们给变量赋值,只能直接指定值 2 var a = 1; 3 var b = 2; 4 var c = 3; 5 console.log(a,b,c); // 1 2 3 6 7 // 现在用解构赋值的写法就变得简单了,只要模式匹配上了就行了,如下 8 // 注意数组是有顺序的 9 var [a,b,c] = [11,22,33]; 10 console.log(a,b,c); // 11 22 33 11 12 var [b,a,c] = [11,22,33]; 13 console.log(a,b,c); // 22 11 33 14 15 // 当然解构赋值还有嵌套比较复杂的写法,如下 16 let [foo,[[bar],[baz]]] = [111,[[222],[333]]]; 17 console.log(foo,bar,baz); // 111 222 333 18 19 let [head,...foot] = [1,2,3,4]; 20 console.log(head,foot); // 1 [2,3,4] 21 22 // 如果解构不成功,变量的值就等于undefined,如下 23 var [bar3,foo3] = [1000]; 24 console.log(bar3,foo3); // 1000 undefined 25 26 // 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功 27 let [x,y] = [10000,20000,30000]; 28 console.log(x,y); // 10000 20000 29 30 // 默认值可以引用解构赋值的其他变量,但该变量必须已经声明 31 let [a=1,b=a] = [2,3]; 32 console.log(a,b); // 2 3 33 34 // 对象的解构也可以指定默认值 35 var {x,y=5} = {x:1}; 36 console.log(x,y); // 1 5 37 38 //对象的解构赋值解构不仅可以用于数组,还可以用于对象(json) 39 //对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定; 40 //而对象的属性没有次序,变量必须与属性同名,才能取到正确的值 41 var {a,b} = {a:‘apple‘,b:‘banana‘}; 42 console.log(a,b); // apple banana 43 var {b,a} = {a:‘apple‘,b:‘banana‘}; 44 console.log(a,b); // apple banana 45 46 // 如果变量名与属性名不一致,必须写成下面这样 47 let obj = {first:‘hello‘,last:‘world‘}; 48 // first ---> f,那么此时f就是first,而不是undefined了,有点类似别名的概念 49 let {first:f,last} = obj; 50 console.log(f,last); // hello world 51 52 //1.也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。 真正被赋值的是后者,而不是前者 53 //2.v是匹配的模式,n才是变量。真正被赋值的是变量n,而不是模式v。 54 //注意,采用这种写法时,变量的声明和赋值是一体的 55 // v ---> n,那么此时n就是vue,而不是undefined了 56 var {v:n} = {v:‘vue‘,r:‘react‘}; 57 console.log(n); // vue 58 console.log(v); // Uncaught ReferenceError: v is not defined 59 console.log(r); // Uncaught ReferenceError: r is not defined
4. 复制数组
1 // 数组的浅拷贝,引用之间的拷贝,没有实现数组的真正复制 2 var arr1 = [1, 2, 3]; 3 var arr2 = arr1; 4 arr2.push(4); 5 console.log(arr1, arr2); //[1, 2, 3, 4] [1, 2, 3, 4] 6 7 // 复制数组深拷贝,传统做法 8 var arr1 = [1,2,3]; 9 var arr2 = []; 10 //通过for循环遍历之后将arr1数组的每一项赋值给arr2数组的每一项, 就实现了数组的深拷贝,这时候我再去操作arr2的数组的时候,arr1就不会受影响了 11 for(var i=0;i<arr1.length;i++){ 12 arr2[i] = arr1[i]; 13 } 14 // 数组尾部添加 15 arr2.push(4); 16 console.log(arr1,arr2); 17 18 // ES6实现的数组的深拷贝方法1 19 var arr1 = [1,2,3]; 20 var arr2 = Array.from(arr1); 21 // 数组尾部添加 22 arr2.push(100); 23 console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 100] 24 25 // ES6实现的数组的深拷贝方法2 26 var arr1 = [1,2,3]; 27 // 超引用拷贝数组 28 var arr2 = [...arr1]; 29 // 数组尾部添加 30 arr2.push(1000); 31 console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 1000] 32 33 function show(...args){ 34 // 此时这个形式参数就是一个数组,我们可以直接push东西进来,如下 35 args.push(5); 36 console.log(args); 37 } 38 // 调用 39 show(1,2,3,4); // [1, 2, 3, 4, 5]
5. 增加了Map对象,倾向于后台
1 var map = new Map(); 2 // 设置 3 // map.set(name,value); 4 map.set(‘a‘,‘apple‘); 5 map.set(‘b‘,‘banana‘); 6 // 获取 7 // map.get(name); 8 console.log(map.get(‘a‘) + ‘ ‘ + map.get(‘b‘)); 9 // 删除之前map对象 10 console.log(map); 11 // 删除 12 // map.delete(name); 13 map.delete(‘a‘); 14 // 删除之后map对象 15 console.log(map); 16 17 // 注意for..in是不能循环map对象的,不报错也无任何反应,所以下一代码无任何输出,稍微注意下 18 for(var name in map){ 19 console.log(name); 20 } 21 22 // 实体 map对象的循环输出 23 for(var name of map){ 24 //循环出来的结果就是:a,apple b,banana 循环key,value 25 console.log(name); 26 } 27 28 //循环出来的结果就是: a,apple b,banana 循环key,value 29 for(var [key,value] of map.entries()){ 30 console.log(key,value); 31 } 32 33 //只循环key 34 for(var key of map.keys()){ 35 console.log(key); 36 } 37 38 //只循环value 39 for(var val of map.values()){ 40 console.log(val); 41 }
6. for-of循环 ,上一例子也说到了for-of循环map对象输出
1 //for of一个arr对象 2 var arr = [‘红楼梦‘,‘西游记‘,‘三国演义‘,‘水浒传‘,‘火影‘]; 3 //只循环key 0 1 2 3 4 输出key值,也就是下标索引 4 for(var key of arr.keys()){ 5 console.log(key); 6 } 7 //只循环value,注意数组是没有.values() 直接 var value of arr ,输出 红楼梦,西游记,三国演义,水浒传,火影 8 for(var value of arr){ 9 console.log(value); 10 } 11 //循环key,value 12 for(var [key,value] of arr.entries()){ 13 console.log(key,value); 14 } 15 16 //for in循环与for of循环的区别 17 var arr = [‘apple‘,‘banana‘,‘orange‘,‘pear‘]; 18 for(var i in arr){ 19 // i打印出来的就是arr数组对应的索引 20 // 0 1 2 3 21 console.log(i); 22 } 23 for(var i of arr){ 24 // i值打印出来的就是我们想要的数组具体的值 25 // apple banana orange pear 26 console.log(i); 27 } 28 29 //for of不能循环json 30 var json = {‘a‘:‘apple‘,‘b‘:‘banana‘,‘c‘:‘orange‘,‘d‘:‘pear‘}; 31 for(var name in json){ 32 // a b c d 33 console.log(name); 34 // apple 35 console.log(json.a); 36 // pear 37 console.log(json[‘d‘]); 38 } 39 // 注意for..of可以循环arr,但是不可以循环json,会报错,特别注意下 40 for(var name of json){ 41 Uncaught TypeError: undefined is not a function 42 console.log(json); 43 }
7. 箭头函数:引入箭头函数有两个方面的作用:更简短的函数并且不绑定this
。
箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数,并且它们不能用作构造函数,不能使用new。
箭头函数的写法 function(){ } 变成 ()=>{ }
1 var a = ()=>{ 2 return 1; 3 }
等价于
1 function a(){ 2 return 1; 3 }
箭头函数不绑定arguments,取而代之用rest参数…解决
1 function A(a){ 2 console.log(arguments); //[object Arguments] [1, 2, 3] 3 } 4 5 var B = (b)=>{ 6 console.log(arguments); //错误:ReferenceError: arguments is not defined 7 } 8 9 var C = (...c)=>{ //...c即为rest参数 10 console.log(c); //[3, 1, 2] 11 } 12 A(1,2,3); 13 B(2,1,3); 14 C(3,1,2);
箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值
1 var obj = { 2 a: 10, 3 b: function(){ 4 console.log(this.a); //输出10 5 }, 6 c: function() { 7 return ()=>{ 8 console.log(this.a); //输出10,捕获了上面obj的this作为自己的this 9 } 10 } 11 } 12 obj.b(); 13 obj.c()();
所谓箭头函数的 this 捕获的是所在的上下文,比如下面这个例子:b
是一个箭头函数,然后它的 this
是指向window
,这是为什么呢,因为箭头函数捕获的是obj{}
这个对象的环境,然后这个环境的this
指向的是window
,就相当于上一条的例子:在c
方法里面return
的那个箭头函数捕获的是c:function(){}
这个环境的this
,而这个环境的this
是obj
1 var obj = { 2 a: 10, 3 b: () => { 4 console.log(this.a); //undefined 5 console.log(this); //window 6 }, 7 c: function() { 8 console.log(this.a); //10 9 console.log(this); //obj{...} 10 } 11 } 12 obj.b(); 13 obj.c();
对于函数的this
指向问题:
- 箭头函数的
this
永远指向其上下文的this
,任何方法都改变不了其指向,如call(), bind(), apply()
- 普通函数的
this
指向调用它的那个对象
8. 对象的简洁语法
1 //传统对象_单体模式写法 key-value模式 2 var person = { 3 name:‘krry‘, 4 age:21, 5 showName:function(){ 6 return this.name; 7 }, 8 showAge:function(){ 9 return this.age; 10 } 11 }; 12 // 调用 13 console.log(person.showName()); // krry 14 console.log(person.showAge()); // 21 15 16 //ES6_单体模式写法 不需要写key 17 var name = ‘krry‘; 18 var age = 21; 19 var person = { 20 name, 21 age, 22 showName(){ 23 return this.name; 24 }, 25 showAge(){ 26 return this.age; 27 } 28 }; 29 // 调用 30 console.log(person.showName()); // krry 31 console.log(person.showAge()); // 21
9. 类和继承(class和extends)
1. 传统面向对象的写法:
1 function Person(name,age){ // 类、构造函数 2 this.name = name; 3 this.age = age; 4 } 5 Person.prototype.showName = function(){ 6 return this.name; 7 }; 8 Person.prototype.showAge = function(){ 9 return this.age; 10 }; 11 var p1 = new Person(‘allen‘,28); 12 var p2 = new Person(‘xiaoxiaoyou‘,101); 13 console.log(p1.showName()); // allen 14 console.log(p2.showAge()); // 101 15 console.log(p1.showName == p2.showName); //true 注意不是调用方法,没有括号,所以才true 16 console.log(p1.constructor == Person); // true 构造方法相等
2. ES6面向对象写法:
1 class Person{ 2 // 构造器 3 constructor(name,age){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 var p1 = new Person(‘aaa‘,18); 15 var p2 = new Person(‘bbb‘,20); 16 console.log(p1.name); // aaa 17 console.log(p1.showName()); // aaa 18 console.log(p2.showAge()); // 20 19 console.log(p1.showAge == p2.showAge); // true 20 console.log(p1.constructor == Person); // true
3. 面向对象给class赋值默认值:
1 class Person{ 2 // 构造器 3 constructor(name=‘default‘,age=0){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 15 var p1 = new Person(); 16 console.log(p1.name); // 构造器里面给的默认值 default 17 console.log(p1.age); // 构造器里面给的默认值 0
4. 传统写法原型继承extends
1 //传统写法原型继承 2 function Person(name,age){ // 类、构造函数 3 this.name = name; 4 this.age = age; 5 } 6 Person.prototype.showName = function(){ 7 return this.name; 8 }; 9 Person.prototype.showAge = function(){ 10 return this.age; 11 }; 12 // 工人类 13 function Worker(name,age){ 14 // 属性继承过来 15 Person.apply(this,arguments); 16 } 17 // 原型继承 18 Worker.prototype = new Person(); 19 var p1 = new Person(‘allen‘,28); 20 var w1 = new Person(‘worker‘,1000); 21 console.log(w1.showName()); // 确实继承过来了 result:worker
5. ES6中面向对象实现类继承
1 class Person{ 2 // 构造器 3 constructor(name,age){ 4 this.name = name; 5 this.age = age; 6 } 7 showName(){ 8 return this.name; 9 } 10 showAge(){ 11 return this.age; 12 } 13 } 14 class Worker extends Person{ 15 constructor(name,age,job=‘啦啦啦‘){ 16 // 继承超父类的属性 17 super(name,age); 18 this.job = job; 19 } 20 showJob(){ 21 return this.job; 22 } 23 } 24 var p1 = new Person(‘aaa‘,18); 25 var w1 = new Person(‘www‘,36); 26 var w2 = new Worker(‘wwwwwwww‘,90); 27 console.log(w1.showName()); // www 28 console.log(w2.showJob()); // 默认给的值 ‘啦啦啦’
10. 模块化 export 和 import
import 导入模块、export 导出模块
可以直接在任何变量或者函数前面加上一个 export
关键字,就可以将它导出。
在一个文件中:
1 export const sqrt = Math.sqrt; 2 export function square(x) { 3 return x * x; 4 } 5 export function diag(x, y) { 6 return sqrt(square(x) + square(y)); 7 }
然后在另一个文件中这样引用:
1 import { square, diag } from ‘lib‘; 2 console.log(square(11)); // 121 3 console.log(diag(4, 3));
总结:
1 //mod.js 2 // 第一种模块导出的书写方式(一个个的导出) 3 // 导出普通值 4 export let a = 12; 5 export let b = 5; 6 // 导出json 7 export let json = { 8 a, 9 b 10 }; 11 // 导出函数 12 export let show = function(){ 13 return ‘welcome‘; 14 }; 15 // 导出类 16 export class Person{ 17 constructor(){ 18 this.name = ‘jam‘; 19 } 20 showName(){ 21 return this.name; 22 } 23 } 24 25 //index.js 26 //导出模块如果用default了,引入的时候直接用,若没有用default,引入的时候可以用{}的形式 27 // 导入模块的方式 28 import { 29 a, 30 b, 31 json, 32 show, 33 Person 34 } from ‘./mod.js‘; 35 console.log(a); // 12 36 console.log(b); // 5 37 console.log(json.a); // 12 38 console.log(json.b); // 5 39 console.log(show()); // welcome 40 console.log(new Person().showName()); // jam 41 42 //mod1.js 43 // 第二种模块导出的书写方式 44 let a = 12; 45 let b = 5; 46 let c = 10; 47 export { 48 a, 49 b, 50 c as cc // as是别名,使用的时候只能用别名,特别注意下 51 }; 52 53 //index1.js 54 // 导入模块的方式 55 import { 56 a, 57 b, 58 cc // cc是导出的,as别名 59 } from ‘./mod1.js‘; 60 console.log(a); // 12 61 console.log(b); // 5 62 console.log(cc); // 10 63 64 //mod2.js 65 // 第三种模块导出的书写方式 ---> default 66 // default方式的优点,import无需知道变量名,就可以直接使用,如下 67 // 每个模块只允许一个默认出口 68 var name = ‘jam‘; 69 var age = ‘28‘; 70 export default { 71 name, 72 age, 73 default(){ 74 console.log(‘welcome to es6 module of default...‘); 75 }, 76 getName(){ 77 return ‘bb‘; 78 }, 79 getAge(){ 80 return 2; 81 } 82 }; 83 84 //index2.js 85 // 导入模块的方式 86 import mainAttr from ‘./mod2.js‘; 87 var str = ‘ ‘; 88 // 直接调用 89 console.log(`我的英文名是:${mainAttr.name}我的年龄是${mainAttr.age}`); 90 mainAttr.default(); // welcome to es6 module of default... 91 console.log(mainAttr.getName()); // bb 92 console.log(mainAttr.getAge()); // 2 93 94 //mod3.js 95 var name = ‘jam‘; 96 var age = ‘28‘; 97 export function getName(){ 98 return name; 99 }; 100 export function getAge(){ 101 return age; 102 }; 103 104 //index3.js 105 // 导入模块的方式 106 import * as fn from ‘./mod3.js‘; 107 // 直接调用 108 console.log(fn.getName()); // jam
11. Promise
在promise之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。通过Promise机制,扁平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,极大的降低了代码耦合性而提高了程序的可扩展性。
就是用同步的方式去写异步代码。
1 //Promise对象 ---> 用来传递异步操作过来的数据的 2 //Pending(等待、处理中) ---> Resolve(完成,fullFilled) ---> Reject(拒绝,失败) 3 //这里只是定义,还没开始执行 4 var p1 = new Promise(function(resolve,reject){ 5 resolve(1); // 成功了,返回一个promise对象1 6 // reject(2); // 失败了 7 }); 8 9 // 接收成功和失败的数据,通过then来传递 10 // then也是返回一个promise对象,会继续往下传递数据,传递给下一个then 11 p1.then(function(value){ 12 // resolve 13 console.log(value); //执行打印1 14 return value + 1; // 1 15 alert(`成功了:${value}`); 16 },function(value){ 17 // reject 18 alert(`失败了:${value}`); 19 }).then(function(value){ 20 console.log(value); // 2 21 }); 22 23 //catch捕获异常错误 24 var p1 = new Promise(function(resolve,reject){ 25 resolve(‘成功了‘); //返回一个promise对象“成功了” 26 }); 27 //then也是返回一个promise对象,会继续往下传递数据 28 p1.then(function(value){ 29 console.log(value); //打印“成功了” 30 // throw是用来抛错误的 31 throw ‘发生了点小意外‘; 32 }).catch(function(e){ 33 // catch用来捕获这个错误的 ---> 追踪 34 console.log(e); 35 }); 36 37 //all ---> 全部,用于将多个promise对象,组合,包装成 38 //Promise.all([p1,p2,p3,...]); 所有的promise对象,都正确,才走成功 39 //否则,只要有一个错误,就走失败 40 var p1 = Promise.resolve(1); 41 var p2 = Promise.reject(0); 42 Promise.all([true,p1,p2]).then(function(obj){ 43 console.log(`成功了:${obj}`); 44 },function(obj){ 45 console.log(`失败了:${obj}`); 46 }); 47 48 // race ---> 返回的也是一个promise对象 49 //最先执行的的promise结果,哪个最快我用哪个,所以下面打印的是one 50 var p1 = new Promise(function(resolve,reject){ 51 setTimeout(resolve,50,‘one‘); 52 }); 53 var p2 = new Promise(function(resolve,reject){ 54 setTimeout(resolve,100,‘two‘); 55 }); 56 Promise.race([p1,p2]).then(function(val){ 57 console.log(val); 58 }); 59 60 //resolve ---> 生成一个成功的promise对象 61 //语法规则:Promise.resolve(val); // 普通值 62 // Promise.resolve(arr); // 数组之类 63 //Promise.resolve(promise); // 传递另一个promise对象 64 //传递普通值 65 Promise.resolve(‘success‘).then(function(val){ 66 // 注意resolve,走得是这里 67 console.log(val); // success 68 },function(err){ 69 console.log("err:"+ err); 70 }); 71 //传递数组 72 Promise.resolve([1,2,3]).then(function(val){ 73 // 注意resolve,走得是这里 74 console.log(val); // [1,2,3] 75 },function(err){ 76 console.log(err); 77 }); 78 //传递一个promise对象 79 var p1 = Promise.resolve(520); 80 var p2 = Promise.resolve(p1); 81 p2.then(function(val){ 82 //从p1那边传递过来的 83 console.log(val); // 520 84 });
再来一道经典面试题:
1 setTimeout(function() { 2 console.log(1) 3 }, 0); 4 new Promise(function executor(resolve) { 5 console.log(2); 6 for( var i=0 ; i<10000 ; i++ ) { 7 i == 9999 && resolve(); 8 } 9 console.log(3); 10 }).then(function() { 11 console.log(4); 12 }); 13 console.log(5);
首先先碰到一个 setTimeout,于是会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,因此开始肯定不会输出 1 。
然后是一个 Promise,里面的函数是直接执行的,因此应该直接输出 2 3 。
然后,Promise 的 then 应当会放到当前 tick 的最后,但是还是在当前 tick 中。
因此,应当先输出 5,然后再输出 4 。
最后在到下一个 tick,就是 1 。
“2 3 5 4 1”
12. Generator、yield
生成器( generator)是能返回一个迭代器的函数。生成器函数也是一种函数,最直观的表现就是比普通的function多了个星号*,在其函数体内可以使用yield关键字,有意思的是函数会在每个yield后暂停。
这里生活中有一个比较形象的例子。咱们到银行办理业务时候都得向大厅的机器取一张排队号。你拿到你的排队号,机器并不会自动为你再出下一张票。也就是说取票机“暂停”住了,直到下一个人再次唤起才会继续吐票。
当你调用一个generator时,它将返回一个迭代器对象。这个迭代器对象拥有一个叫做next的方法来帮助你重启generator函数并得到下一个值。next方法不仅返回值,它返回的对象具有两个属性:done和value。value是你获得的值,done用来表明你的generator是否已经停止提供值。继续用刚刚取票的例子,每张排队号就是这里的value,打印票的纸是否用完就这是这里的done。
// 生成器 function *createIterator() { yield 1; yield 2; yield 3; } // 生成器能像正规函数那样被调用,但会返回一个迭代器 let iterator = createIterator(); console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 console.log(iterator.next().value); // 3 console.log(iterator.next().value); // undefined 因为generator已经停止提供值
1 //Generator ---> 生成器就是一个函数 2 //特点: 3 //1.函数名前面带一个*,和普通函数做区分 4 //2.内部使用yield语句 5 //调用方式,如下var res = show(); 与普通函数一样 6 //value指的是generator函数内容yield定义的值,done:false表示还没遍历完 7 //直接找到返回值return了,那么此时done才会为true 8 //console.log(res.next());{value:‘值1‘,done:false} 9 function* show(){ 10 yield ‘Hello‘; 11 yield ‘World‘; 12 yield ‘ES6‘; 13 return ‘xx‘; 14 } 15 var res = show(); 16 console.log(res.next()); // {value: "Hello", done: false} 17 console.log(res.next()); // {value: "World", done: false} 18 console.log(res.next()); // {value: "ES6", done: false} 19 console.log(res.next()); // {value: "allen", done: true} 20 // 已经找到return返回值了,继续下去就没有意义了 21 // console.log(res.next()); // {value: "undefined", done: true} 22 23 //yield本身没有返回值,或者可以说每次给你返回的是undefined 24 function* show(){ 25 var a = yield ‘Hello‘; 26 return a; 27 } 28 var res = show(); 29 console.log(res.next()); // {value: "Hello", done: false} 30 console.log(res.next()); // {value: "undefined", done: true} 31 32 //next方法是可以带参数的,死循环的generator函数 33 function* fn(){ 34 for(var i=0;true;i++){ 35 // 如果里面传了一个值,那么它会把这个参数赋给最近的一个yield 36 var a = yield i; 37 if(a) i = -1; 38 } 39 } 40 var d = fn(); 41 console.log(d.next()); // {value: 0, done: false} 42 console.log(d.next()); // {value: 1, done: false} 43 console.log(d.next()); // {value: 2, done: false} 44 // 如果里面传了一个值,那么它会把这个参数赋最近的一个yield 45 console.log(d.next(true)); // {value: 0, done: false} 46 console.log(d.next()); // {value: 1, done: false} 47 console.log(d.next()); // {value: 2, done: false} 48 console.log(d.next()); // {value: 3, done: false} 49 50 // for..0f循环generator函数 51 function* fn(){ 52 yield 1; 53 yield 2; 54 yield 3; 55 yield 4; 56 yield 5; 57 return 6; 58 } 59 //for..0f循环generator函数,可以取值 60 for(let val of fn()){ 61 document.write(val); // 12345 62 } 63 64 // 对象里使用generator函数的特殊写法,注意下 65 var json = { 66 *show(){ 67 yield ‘a‘; 68 yield ‘b‘; 69 return ‘c‘; 70 } 71 }; 72 var res = json.show(); 73 console.log(res.next()); // {value: "a", done: false} 74 console.log(res.next()); // {value: "b", done: false} 75 console.log(res.next()); // {value: "c", done: true}
自动调用生成器并启动迭代器的方法:
1 function run(taskDef) { //taskDef即一个生成器函数 2 3 // 创建迭代器,让它在别处可用 4 let task = taskDef(); 5 6 // 启动任务 7 let result = task.next(); 8 9 // 递归使用函数来保持对 next() 的调用 10 function step() { 11 12 // 如果还有更多要做的 13 if (!result.done) { 14 console.log(result.value); //这里就执行该做的事 15 result = task.next(); 16 step(); 17 } 18 } 19 20 // 开始处理过程 21 step(); 22 23 } 24 //生成器 25 function *createIterator() { 26 yield 1; 27 yield 2; 28 yield 3; 29 } 30 //启动 31 run(createIterator);