在ES6模块化规范诞生之前,javaScript社区已经尝试并提出了AMD、CMD、CommonJS等模块化规范。
但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通过的模块化标准,例如:
太多的模块化规范给开发者增加了学习的难度与开发的成本。因此,大一统的ES6模块化规范诞生了!
ES6模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不需要再额外学习AMD、CMD或CommonJS等模块化规范。
ES6模块化规范中定义:
node.js 中默认仅支持CommonJS模块化规范,若想基于node.js体验与学习ES6的模块化语法,可以按照如下两个步骤配置:
1)确保安装了v14.15.1或者更高版本的node.js
终端输入: node -V 或者 node --version
2)在pakage.json 的根节点中添加
"type":"module" //节点
ES6的模块化主要包含如下3种用法:
1)默认导出与默认导入
2)按需导出与按需导入
3)直接导入并执行模块中的代码
默认导出的语法: export default 默认导出的成员
let n1 = 10
let b2 = 10
function show(){
}
export default { //使用 export default 默认导出语法,向外共享n1 和 show两个成员
n1,
show
}
----------------------分割线
//导入
import m1 from './xxxx.js' //导入后m1中就拿到了导出的值
每个模块中,只允许使用唯一的一次export default,否则会报错!
默认导入时的接收名称可以任意名称,只要是合法的成员名称即可:
//假设当前模块是test.js
//向外按需导出变量s1
export let s1 = 'aaa'
//向外按需导出方法
export function say(){}
语法: import {s1} from ‘模块标识符’
当有多个成员时: import {s1,s2,say} from ‘./test.js’
1)每个模块中可以使用多次按需导出
2) 按需导入的成员名称必须和按需导出的名称保持一致
3)按需导入时,可以使用as关键字进行重命名 : import {s1 as str1,s2,say} from ‘./test.js’
4)按需导入可以和默认导入一起用
export let s1 = 'aaa'
export let s2 = 'bbb'
export default {
a:20,
}
--------------分割线
import info ,{s1,s2 as str2,say} form '模块.js' //注意花括号内是按需导出的变量,花括号外的info则指向默认导出的变量,此时info为空
如果只想单纯的执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码,示例代码如下:
//当前文件模块化为05_m3.js
//在当前模块执行一个for 循环操作
for(let i = 0;i < 3;i++){
console.log(i)
}
-------------------分割线
//直接导入并执行模块代码,不需要得到模块向外共享的成员。此时,可以直接导入并执行模块代码,示例代码如下:
//当前文件模块为05_m3.js
//在当前模块中执行一个for循环操作
for(let i=0;i<3;i++){
console.log(i)
}
//--------------分割线
//直接导入并执行模块代码,不需要得到模块向外共享的成员
import './05_m3.js'
多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下:
setTimeout(()=>{ //第一层回调函数
console.log('延时1s后输出')
setTimeout(()=>{ //第二层回调函数
console.log('再延时两秒后输出')
setTimeout(()=>{
console.log('再延时3秒后输出')
},3000)
},2000)
},1000)
回调地狱的缺点:
为了解决回调地狱的问题,ES6(ECMAScript 2015) 中新增了Promise的概念。
1)promise是一个构造函数
const p = new Promise({xxx})
p.then(r1=>{
return new promise({xxx})
})
.then(r2=>{
return new promise({xxx})
}).catch(error=>{
})
//使用catch可以捕获错误
Promise.all()方法会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制)。示例代码如下:
//1.定义一个数组,存放3个文件的异步操作
const promiseArr = [
thenFs.readFile('./files/11.txt','utf8'),
thenFs.readFile('./files/22.txt','utf8'),
thenFs.readFile('./files/33.txt','utf8')
]
// 2.将promise的数组,作为Promise.all()的参数
Promise.all(promiseArr)
.then(([r1,r2,r3])=>{
//2.1所有文件读取成功(等待机制)
console.log(r1,r2,r3)
})
.catch(err => {
//2.2捕获Promise异步操作中的错误
console.log(err.message)
})
promise.race() 方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then操作(赛跑机制)。示例代码如下:
//1.定义一个数组,存放3个文件的异步操作
const promiseArr = [
thenFs.readFile('./files/11.txt','utf8'),
thenFs.readFile('./files/22.txt','utf8'),
thenFs.readFile('./files/33.txt','utf8')
]
// 2.将promise的数组,作为Promise.race()的参数
Promise.race(promiseArr)
.then(result)=>{
//2.1只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制)
console.log(r1,r2,r3)
})
.catch(err => {
//2.2捕获Promise异步操作中的错误
console.log(err.message)
})
//1.方法的名称为 getFile
//2. 方法接收一个形参fpath,表示要读取的文件的路径
function getFile(fpath){
//3.方法的返回值为 Promise的实例对象
return new Promise()
}
如果想要创建具体的异步操作,则需要在new Promise() 构造函数期间,传递一个function函数,将具体的异步操作定义到function函数内部。示例代码如下:
//1.方法的名称为 getFile
//2. 方法接收一个形参fpath,表示要读取的文件的路径
function getFile(fpath){
//3.方法的返回值为 Promise的实例对象
return new Promise(function(){
//4.下面这行代码,表示是一个具体的、读文件的异步操作
fs.readFile(fpath,'utf8',(err,dataStr)=>{ })
})
}
getFile('./files/1.txt').then(成功的回调函数,失败的回调函数)
Promise异步操作的结果,可以调用resvole或reject回调函数进行处理。示例代码如下:
function getFile(fpath) {
return new Promise(function(resovle,reject){
fs.readFile(fpath,'utf8',(err,dataStr)=>{
if(err) return reject(err) //如果读取失败,则调用"失败的回调函数"
resolve(dataStr) //如果成功,则调用"成功的回调函数"
})
})
}
getFile('./files/1.txt').then((r1)=>console.log(r1)).catch(err=>console.log(err))
async/await 是ES8(ECMAScript2017) 引入的新语法,用来简化Promise异步操作。在async/await出现之前,开发者只能通过链式.then()方式处理Promise异步操作。示例代码如下:
const p = new Promise({xxx})
p.then(r1=>{
return new promise({xxx})
})
.then(r2=>{
return new promise({xxx})
}).catch(error=>{
})
使用async/await 简化Promise异步操作的示例代码如下:
import thenFs form 'then-fs'
//按照顺序读取 1,2,3的内容
async function getAllFile(){
const r1 = await thenFs.readFile('./files/1.txt','utf8')
console.log(r1)
const r2 = await thenFs.readFile('./files/2.txt','utf8')
console.log(r2)
const r3 = await thenFs.readFile('./files/3.txt','utf8')
console.log(r3)
}
getAllFile()
1)如果在function中使用了await,则function必须被async修饰
2)在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行
console.log('A')
async function getAllFile(){
console.log('B')
const r1 = await thenFs.readFile('./files/1.txt','utf8')
const r2 = await thenFs.readFile('./files/2.txt','utf8')
const r3 = await thenFs.readFile('./files/3.txt','utf8')
console.log(r1,r2,r3)
console.log('D')
}
getAllFile()
console.log('C')
//执行顺序 A B C r1 r2 r3 D
javascript是一门单线程的编程语言。也就是说,同一时间只能做一件事情。
javascript的执行线程:
任务1 -------> 任务2------> 任务3 ------> 任务N
待执行的任务队列
为了防止某个耗时的任务导致程序假死的问题,javascript把待执行的任务分成了两类:
1)同步任务 (synchronous)
1)同步任务由javacript主线程次序执行
2)异步任务委托给宿主环境执行
3)已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行。
4) javacript主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
5)javacript主线程不断重复上面的第四步
javascript把异步任务又做了进一步划分,异步任务又分为两类,分别是:
1)宏任务(macrotask)
每一个宏任务执行完成之后,都会检查是否存在待执行的微任务,如果有,则执行完所有的微任务之后,再继续执行下一个宏任务。
宏任务和微任务都是指异步任务的执行方式。
宏任务指的是由浏览器或Node.js环境提供的异步任务,比如setTimeout、setInterval、I/O操作等。当宏任务被加入到任务队列中后,等待JavaScript引擎的执行。
微任务指的是由JavaScript引擎提供的异步任务,比如Promise、MutationObserver等。当微任务被加入到任务队列中后,会在当前宏任务执行完毕后立即执行。
执行顺序:当一个宏任务执行完毕后,JavaScript引擎会先执行所有微任务,然后再执行下一个宏任务。这意味着在同一个宏任务中,微任务总是优先于下一个宏任务执行。而不同宏任务之间的执行顺序是不确定的,取决于它们被加入到任务队列中的先后顺序
setTimeout(function(){
console.log('1')
})
new Promise(function(resolve){
console.log('2')
resolve()
}).then(function(){
console.log('3')
})
console.log('4')
//执行顺序 2 4 3 1
分析:
1)先执行所有的同步任务,new Promise是个同步任务,new完后里面的function会立即执行 所以先输出2,然后同步任务4
2)再执行微任务
promise.then里面就是个微任务 所以输出3
3)再执行宏任务
setTimeout 输出 1
console.log('1')
setTimeout(function(){
console.log('2') //
new Promise(function(resolve){
console.log('3')//
resolve()
}).then(function(){
console.log('4') //
})
})
new Promise(function(resolve){
console.log('5')
resolve()
}).then(function(){
console.log('6')
})
setTimeout(function(){
console.log('7')//
new Promise(function(resolve){
console.log('8') //
resolve()
}).then(function(){
console.log('9') //
})
})
以上代码输出结果: 1 5 6 2 3 4 7 8 9