JavaScript-22
1.ES6模块
- 在ES6前,实现模块化使用的是RequireJS或者是seaJS(分别是基于AMD规范的模块化库,和基于CMD规范的模块化库)
- ES6引入了模块化,其设计思想是在编译时就能确定的依赖关系,以及输入输出的变量
- ES6的模块化分为导入和导出两个模块
- 特点
- ES6模块自动开启严格模式
- 模块可以导入和导出各种类型的变量
- 每个模块都有自己的上下文,每一个模块内声明的都是局部变量
- 每个模块只加载一次(是单例的),若再去加载同目录下的文件,直接从内存中读取
- 基本用法
- 导出的函数声明与类声明必须要有名称(export default命令另外考虑)
- 不仅能导出声明还能导出引用
- export命令可以出现在模块的额任何外置,但必须位于模块顶层
- import命令会提升到整个模块的头部,首先执行
- 加以使用大括号指定所要输出的一组变量写在文档尾部,明确导出的接口
- 函数与类都需要有对应的名称,导出文档尾部也避免了无对应名称
1 //test.js 2 let myName = 'Tom'; 3 let myAge = 20; 4 let fn = function(){ 5 return "my name is "+myName+" and I'm "+myAge; 6 } 7 let myClass = class myClass{ 8 static a = 'yeah!'; 9 } 10 export{myName,myAge,fn,myClass}
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 <!--注意type为module--> 9 <script type="module"> 10 import{myName,myAge,fn,myClass} from "./js/test.js"; 11 console.log(myName);//Tom 12 </script> 13 </body> 14 </html>
- as的使用
1 let myName = 'Tom'; 2 // 使用as重新定义导出的接口名称,隐藏模块的内部变量 3 export{myName as name}
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 <script type="module"> 9 import{name} from "./js/test.js"; 10 console.log(name);//Tom 11 </script> 12 </body> 13 </html>
- import命令的特点
- 只读属性,不允许在家在模块的脚本里面,改写接口的引用指向,既可以改写import变量类型为对象的属性值,不能改写import变量类型为基本类型的值
- 单例模式,多次重复执行同一句import语句,那么只会执行一次,而不会执行多次,声明不同接口引用,会声明对应变量但只执行一次import,import是静态执行,所以不能使用表达式和变量
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 </head> 7 <body> 8 <script type="module"> 9 import{name} from "./js/test.js"; 10 import{myAge} from "./js/test.js"; 11 //相当于import{name,myAge} from "./js/test.js"; 12 </script> 13 </body> 14 </html>
- export default命令
- 在同一个文件或模块中,export、import可以有多个,export default仅有一个
- export default中的default是对应的导出接口变量
- 通过export方式导出,在导入时要加{},export default则不需要
- export default向外暴露成员,可以使用任意变量来接受
1 var a = "kiki"; 2 export default a;//仅有一个 3 import b from "./test.js" 4 //不需要加{}使用任意变量接收
- 复合使用
- 可以将导出接口改名,包括default
- 复合使用export与import,也可以导出全部,当前模块导出的接口会覆盖继承导出的
1 export {foo, bar} from "methods" 2 //约定于下面两段语句,不过上面导入导出方式该模块没有导入foo与bar 3 import {foo, bar} from "methods" 4 export {foo, bar} 5 /** 特点1 **/ 6 //普通改名 7 export {foo as a} from "methods" 8 //将foo转导成default 9 export {foo as default} from "methods" 10 //将default转导成foo 11 export {default as foo} from "methods" 12 /** 特点2 **/ 13 export * from "methods"
2.ES6 Promise对象
是异步编程的一种解决方案。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
- Promise状态:除了异步操作的结果,任何其他操作都无法改变这个状态
- pending:进行中
- fulfilled:已成功
- rejected:已失败
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //promise对象只有:从pending变为fulfilled和从pending变为rejected状态 10 // 只要处于fulfilled和rejected,状态就不会再改变了即resolved(已定型) 11 const p1 = new Promise(function(resolve,reject){ 12 resolve('success1'); 13 resolve('success2'); 14 }) 15 const p2 = new Promise(function(resolve,reject){ 16 resolve('success3'); 17 resolve('reject'); 18 }) 19 p1.then(function(value){ 20 console.log(value);//success1 21 }) 22 p2.then(function(value){ 23 console.log(value);//success3 24 }) 25 </script> 26 </body> 27 </html>
- 状态的缺点
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消
- 如果不设置回调函数,Promise内部抛出错误,不会反映到外部
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
- then方法
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //then方法接受两个函数作为参数 10 //第一个参数:Promise执行成功时的回调 11 //第二个参数:Promise执行失败后的回调 12 //两个函数只有一个会被调用,在JS事件队列的当前运行完成之前,回调函数永远不会被调用 13 const p = new Promise(function(resolve,reject){ 14 resolve("success"); 15 }) 16 p.then(function(value){ 17 console.log(value); 18 }) 19 console.log('first'); 20 //first 21 //success 22 //注意:通过.then添加的回调函数,不论什么时候都会被调用, 23 //可以添加多个回调函数,他们会按照插入顺序并且独立运行 24 </script> 25 </body> 26 </html>
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 const p = new Promise(function(resolve,reject){ 10 resolve(1); 11 }).then(function(value){//第一个then 12 console.log(value); 13 return value*2; 14 }).then(function(value){//第二个then 15 console.log(value); 16 }).then(function(value){//第三个then 17 return Promise.resolve('resolve'); 18 }).then(function(value){//第四个then 19 console.log(value); 20 return Promise.reject('reject'); 21 }).then(function(value){//第五个then 22 console.log('resolve: '+value); 23 },function(err){ 24 console.log('reject',err); 25 }) 26 /* 27 打印结果: 28 1 29 2 30 resolve 31 reject reject 32 then方法将返回一个resolved或rejected状态的Promise对象便于链式调用, 33 且Promise对象的值也是这个返回值 34 */ 35 </script> 36 </body> 37 </html>
- then方法注意点
- 简便的Promise链式编程最好扁平化,不要嵌套Promise。注意总是返回或终止Promise链
3.Generator函数
ES6引入了Generator函数,可以通过yield关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供了解决方案
- Generator函数组成
- 一是在function后面,函数名之前有个*
- 函数内部有yield表达式(定义函数内部的状态)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 function* func(){ 10 console.log("one"); 11 yield'1'; 12 console.log("two"); 13 yield '2'; 14 console.log("three"); 15 return '3'; 16 } 17 </script> 18 </body> 19 </html>
- 执行机制
- 调用Gnerator函数和调用普通函数一样,在函数名后面加上()即可。但是Generator函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象的next方法,指针就会从函数头部或者上一次停下来的地方开始执行
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 function* func(){ 10 console.log("one"); 11 yield '1'; 12 console.log("two"); 13 yield '2'; 14 console.log("three"); 15 return '3'; 16 } 17 var f = func(); 18 //第一次调用next方法时,从Generator头部开始执行,打印了'one' 19 //执行到yield后就停下来,yield后面的值作为返回对象的value属性 20 //此时函数没有执行完,返回对象的done为false 21 console.log(f.next()); 22 //one 23 //{value: '1', done: false} 24 console.log(f.next()); 25 //two 26 //{value: '2', done: false} 27 console.log(f.next()); 28 //three 29 //{value: '3', done: true} 30 </script> 31 </body> 32 </html>
- 函数返回遍历器对象的方法
- next方法
- 当next的方法不传入参数时,yiled的返回值是undefined
- 当next传入参数时,该参数会作为上一步yiled的返回值
- next方法
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 function* sendParameter(){ 10 console.log("start"); 11 var x = yield '1'; 12 console.log("one: "+x); 13 var y = yield '2'; 14 console.log("two: "+y); 15 console.log("total: "+(x+y)); 16 } 17 //next不传参 18 var sendp1 = sendParameter(); 19 console.log(sendp1.next()); 20 //start 21 //{value: '1', done: false} 22 console.log(sendp1.next()); 23 //one: undefined 24 //{value: '2', done: false} 25 console.log(sendp1.next()); 26 //two: undefined 27 //total: NaN 28 //{value: undefined, done: true} 29 //next传参 30 var sendp2 = sendParameter(); 31 console.log(sendp2.next(10)); 32 //start 33 //{value: '1', done: false} 34 console.log(sendp2.next(20)); 35 //one: 20 36 //{value: '2', done: false} 37 console.log(sendp2.next(30)); 38 //two: 30 39 //total: 50 40 //{value: undefined, done: true} 41 //除了使用next,还可以使用for…of…循环遍历Generator函数产生的Iterator对象 42 </script> 43 </body> 44 </html>
- return方法
- return方法返回给定值,并结束遍历Generator函数
- return方法提供参数时,返回该参数;不提供参数时,返回undefined
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 function* foo(){ 10 yield 1; 11 yield 2; 12 yield 3; 13 } 14 var f = foo(); 15 console.log(f.next());//{value: 1, done: false} 16 console.log(f.return("foo"));//{value: 'foo', done: true} 17 console.log(f.next());//{value: undefined, done: true} 18 console.log("-------------------------------"); 19 //throw 20 //throw方法可以在Generator函数体外面抛出异常,在函数体内部捕获 21 //遍历对象抛出两个错误,第一个Generator函数内部捕获, 22 //第二个因为函数体内部的catch已经执行过了所以不会再捕获这个错误, 23 //所以这个错误抛出Generator函数体,被函数体外部的catch捕获 24 var g = function* (){ 25 try{ 26 yield; 27 }catch(e){ 28 console.log('catch inner',e); 29 } 30 }; 31 var i = g(); 32 i.next() 33 try{ 34 i.throw("a"); 35 i.throw("b"); 36 }catch(e){ 37 console.log('catch outsider',e); 38 } 39 //catch inner a 40 //catch outsider b 41 </script> 42 </body> 43 </html>
- yield*表达式
- yield表达式表示yield返回一个遍历器对象,用于在Generator函数内部,调用另一个Generator函数
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 function* callee(){ 10 console.log('callee: '+(yield)); 11 } 12 function* caller(){ 13 while(true){ 14 yield* callee(); 15 } 16 } 17 const callerObj = caller(); 18 console.log(callerObj.next()); 19 //{value: undefined, done: false} 20 console.log(callerObj.next('a')); 21 //callee: a 22 //{value: undefined, done: false} 23 console.log(callerObj.next('b')); 24 //callee: b 25 //{value: undefined, done: false} 26 //等同于 27 // function* caller(){ 28 // while(true){ 29 // for(var value of callee){ 30 // yield value; 31 // } 32 // } 33 // } 34 </script> 35 </body> 36 </html>
- 使用场景
- 为不具备Iterator接口的对象提供遍历方法
4.ES6 async函数
async函数时ES7才有的与异步操作有关的关键字,和Promise,Generator有很大的关联
- async function name([param[,param[… param]]]){statements}
- name:函数名称
- param:要传递给函数的参数名称
- statements:函数体语句
- 返回值
- async函数作为一个Promise对象,可以使用then方法添加回调函数
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 async function helloAsync(){ 10 return 'helloAsync'; 11 } 12 console.log(helloAsync()); 13 //Promise {<fulfilled>: 'helloAsync'} 14 helloAsync().then(v=>{ 15 console.log(v); 16 }) 17 //helloAsync 18 </script> 19 </body> 20 </html>
-
- async函数中可能会有await表达式,在async函数执行时如果遇到await就会先暂停执行,等到触发异步操作完成后,回复async函数的执行并返回解析值。注意:await关键字仅在async function函数体中有效。在此函数体外使用await,会报错:语法错误
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 function testAwait(){ 10 return new Promise((resolve)=>{ 11 setTimeout(function(){ 12 console.log("testAwait"); 13 resolve(); 14 },1000); 15 }); 16 } 17 async function helloAsync(){ 18 await testAwait(); 19 console.log("helloAsync"); 20 } 21 helloAsync(); 22 </script> 23 </body> 24 </html>
- await
- await操作符用于等待一个Promise对象,它只能在异步函数async function中使用
- [return_value] = await expression
- expression:一个Promise对象或者任何要等的值
- await针对所跟不同表达式的处理方式
- Promise对象:await会暂停执行,等待Promise对象resolve,然后回复async函数的执行并返回解析值
- 非Promise对象:执行返回对应值
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 async function a(){ 10 console.log(1); 11 console.log(2); 12 } 13 a(); 14 console.log(3);//1 2 3 15 async function b(){ 16 await 1; 17 console.log(1); 18 console.log(2); 19 } 20 b(); 21 console.log(3);// 3 1 2 22 </script> 23 </body> 24 </html>