ES6模块化

ES6模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不再需要需要额外学习AMD、CMD或CommonJS等模块化规范

ES6模块化规范中定义:

  • 每个js文件都是一个独立的模块
  • 导入其他模块成员使用import关键字
  • 向外共享模块成员使用export关键字

在node.js中体验ES6模块化

  1. 安装v14.15.1或更高版本的node.js
  2. 在package.json的根节点中添加type节点

ES6模块化的三种基本语法:

  1. 默认导出:export default 默认导出的成员
let n1 = 10    //定义模块私有成员 n1
    let n2 = 20  //定义模块私有成员n2(外界访问不到n2,因为它没有被共享出去)
    function show() {}  //定义模块私有方法 show
    
    export default {    //使用export default 默认导出语法,向外共享n1和show两个成员
        n1,
        show
    }



         默认导入:import  接收名称  from ‘模块标识符’

    // 从01.js模块中导入export default 向外共享的成员
    // 使用m1成员进行接收
    import m1 from './01默认导出.js'
    console.log(m1);
    
    // 打印输出结果为:
    // { n1: 10, show: [Function: show] }
注意事项:1.1每个模块中只允许使用唯一一次export default,否则会报错

         1.2默认导入时的接收名称可以任意名称,只要合法即可
  1. 按需导出:export 按需导出的成员
// 当前模块为03.js
    
    // 向外按需导出变量s1
    export let s1 = 'aaa'
    // 向外按需导出变量s2
    export let s2 = 'bbb'
    // 向外按需导出方法 say
    export function say() {}

按需导入:import{s1} from ‘模块标识符’

// 导入模块成员
    import { s1,s2,say } from './03按需导出.js';
    console.log(s1);
    console.log(s2);
    console.log(say);
注意事项:每个模块中可以使用多次按需导出

          按需导入的成员名称必须和按需导出的名称保持一致

          按需导入时,可以使用as关键字进行重命名
              
          按需导入可以和默认导入一起使用
  1. 直接导入并执行模块中的代码
Import ’模块标识符 ’
// 当前文件模块为05.js
    for(let i=0;i<3;i++){
        console.log(i);
    }
    
    // ---------------分割线------------
    
    // 当前文件模块为06.js
    // 直接导入并执行模块代码,不需要得到模块向外共享的成员
    import './05.直接运行模块中的代码.js'

Promise

回调地狱:多层回调函数的相互嵌套

setTimeout(() => { //第1层回调函数
        console.log('延迟1秒后输出');
        setTimeout(() => {//第2层回调函数
            console.log('延迟2秒后输出');
            setTimeout(() => {//第3层回调函数
                console.log('延迟3秒后输出');
            }, 3000)
        }, 2000)
    }, 1000)

回调地狱的缺点:

  • 代码耦合性太强,牵一发而动全身,难以维护
  • 大量冗余的代码相互嵌套,代码的可读性变差

为了解决回调地狱的问题,ES6中新增了promise的概念

promise的基本概念

① Promise 是一个构造函数
⚫ 我们可以创建 Promise 的实例 const p = new Promise()
⚫ new 出来的 Promise 实例对象,代表一个异步操作

② Promise.prototype 上包含一个 .then() 方法
⚫ 每一次 new Promise() 构造函数得到的实例对象,
⚫ 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()

③ .then() 方法用来预先指定成功和失败的回调函数
⚫ p.then(成功的回调函数,失败的回调函数)
⚫ p.then(result => { }, error => { })
⚫ 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

1.基于回调函数按顺序读取文件内容

import fs from 'fs'
    //读取文件1.txt
    fs.readFile('./files/1.txt', 'utf8', (err1, r1) => {
        if (err1) return console.log(err1.message);
        console.log(r1);
        //读取文件2.txt
        fs.readFile('./files/2.txt', 'utf8', (err2, r2) => {
            if (err2) return console.log(err2.message);
            console.log(r2);
            //读取文件3.txt
            fs.readFile('./files/3.txt', 'utf8', (err3, r3) => {
                if (err3) return console.log(err3.message);
                console.log(r3);
            })
        })
    })

2.基于then-fs读取文件内容

由于node,js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持promise的调用方式,所以要先运行如下的命令,安装then-fs这个包

npm install then-fs

3.then-fs的基本使用

调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数

// 基于promise的方式读取文件
        import thenFs from 'then-fs'
        // 注意:.then()中的失败回调是可选的,可以被省略
        thenFs.readFile('./files/1.txt','utf8').then(r1=>{console.log(r1)},err1=>{console.log(err1.message);})
        thenFs.readFile('./files/2.txt','utf8').then(r2=>{console.log(r2)},err2=>{console.log(err2.message);})
        thenFs.readFile('./files/3.txt','utf8').then(r3=>{console.log(r3)},err3=>{console.log(err3.message);})

注意:上述的代码无法保证文件的读取顺序,需要做进一步的改进!

4.then()方法的特性

如果上一个 .then() 方法中返回了一个新的Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题。

5.基于promise按顺序读取文件的内容

promise支持链式调用,从而来解决回调地狱的问题

import thenFs from 'then-fs'
    
    thenFs.readFile('./files/1.txt', 'utf8') //返回值是promise的实例对象
        .then(result1 => {   //通过.then为第一个promise实例指定成功之后的回调函数
            console.log(result1)
            return thenFs.readFile('./files/2.txt', 'utf8') //在第一个.then中返回一个新的prmise实例对象
        })
        
        .then(result2 => { //继续调用.then,为上一个.then的返回值指定成功之后的回调函数
            console.log(result2)
            return thenFs.readFile('./files/3.txt', 'utf8')
        })
        .then(result3 => { console.log(result3) })

6.通过.catch捕获错误

在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:

import thenFs from 'then-fs'
    
    thenFs.readFile('./files/11.txt', 'utf8') 
        .then(result1 => {  
            console.log(result1)
            return thenFs.readFile('./files/2.txt', 'utf8') 
        })
        
        .then(result2 => { 
            console.log(result2)
            return thenFs.readFile('./files/3.txt', 'utf8')
        })
        .then(result3 => { console.log(result3) })
        .catch(err=>{  //捕获第一行发生的错误,并输出错误的信息
            console.log(err.message);
        })

async/await

async/await是ES8引入的新语法,用来简化promise异步操作

1.async/await的基本使用

使用async/await简化promise异步操作的示例代码:

import thenFs from '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()

注意:

① 如果在 function 中使用了 await,则 function 必须被 async 修饰
② 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行

EventLoop

1.javaScript 是单线程的编程语言。也就是说,同一时间只能做一件事情

vue3打包es6转es5_回调函数

单线程执行任务队列的问题:

如果前一个任务非常耗时时,则后续的任务就不得不一直等待,从而导致程序假死的问题

2.同步任务和异步任务

为了防止某个耗时任务导致程序假死的问题,javascript把待执行的任务分成了两类:

① 同步任务

  • 又叫做非耗时任务,指的是在主线程上排队执行的那些任务
  • 只有前一个任务执行完毕,才能执行后一个任务

② 异步任务

  • 又叫做耗时任务,异步任务由javascript委托给宿主环境进行执行
  • 当异步任务执行完成后,会通知javascript主线程执行异步任务的回调函数

3.同步任务和异步任务的执行过程

vue3打包es6转es5_vue3打包es6转es5_02

① 同步任务由 JavaScript 主线程次序执行
② 异步任务委托给宿主环境执行
③ 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
④ JavaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
⑤ JavaScript 主线程不断重复上面的第 4 步

在这里举个好理解的例子

大厨和徒弟,比如大厨在做饭的时候,需要一瓣蒜,他就会让徒弟剥,然后大厨继续做他的事情,徒弟剥好蒜之后,放在一个固定的位置,大厨在做完手头的事情后,看看有没有剥好的蒜,有就拿去用,没有就继续做自己的事

4.EventLoop的基本概念

javaScript主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断的,所以整个的这种运行机制又称为EventLoop(事件循环)

宏任务和微任务

1.javascript把异步任务又做了进一步的划分,异步任务又分为两类:

① 宏任务:

  • 异步Ajax请求
  • setTimeout、setinterval
  • 文件操作
  • 其他宏任务

② 微任务:

  • promise.then、 .catch和.finally
  • process.nextTick
  • 其他微任务

vue3打包es6转es5_javascript_03

2.宏任务和微任务的执行顺序

每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务之后,再继续执行下一个宏任务

vue3打包es6转es5_javascript_04

分析以下代码输出的顺序

setTimeout(function(){
        console.log('1');
    })
    new Promise(function(resolve){
        console.log('2');
        resolve()
    }).then(function(){
        console.log('3');
    })
    console.log('4');

正确的输出顺序是:2431

分析:1.先执行所有的同步任务

2.再执行微任务

3.再执行下一个宏任务