ES6(二)

57 阅读8分钟

promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。

什么是异步编程? 传统方案怎么解决异步编程?

异步编程就是在执行一个指令之后不是马上得到结果,而是继续执行后面的指令,等到特定的事件触发后,才得到结果。之前我们一般通过回调函数的方式解决异步编程。

例如:

$.ajax({
            success:function(){
                $.ajax({
                    function(){
                        $.ajax({
                            function(){
                                 ...   
                            }
                        })
                    }
                })
            }
        })

某些场景下,我们就需要一次又一次的回调…回调到最后,会发现我们的代码就会变成金字塔形状?这种情况被亲称为回调地狱。回调地狱不仅看起来很不舒服,可读性比较差,难以维护,除此之外还有比较重要的一点就是对异常的捕获无法支持

promise的使用

Promise有三种状态

  1. pending: 初始状态,既不是成功,也不是失败状态。
  1. fulfilled: 意味着操作成功完成。
  1. rejected: 意味着操作失败。

语法:

let promise = new Promise(function(resolve, reject) {
  // … 异步操作的代码
  if (/* 异步操作成功 */){
    resolve(value);//这是一个函数,传什么数据都可以
  } else {
    reject(error);
  }
});

Resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

Reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

hen方法是Promise实例状态改变时的回调函数
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式调用写法,即then方法后面再调用另一个then方法。

案例:

let p = new Promise((resolve,reject)=>{
            setTimeout(function(){
                console.log(1);
                resolve()
            },3000)
        })
        .then(()=>{
                return new Promise((resolve,reject)=>{
                    setTimeout(function(){
                    console.log(2);
                    resolve();
                },2000)
            })
        })
        .then(()=>{
                return new Promise((resolve,reject)=>{
                    setTimeout(function(){
                    console.log(3);
                    resolve();
                },1000)
            })
        });

catch

Promise.prototype.catch()方法用于指定发生错误时的回调函数

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

catch和then中第二个函数的区别:

主要区别就是,如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。(本身出问题了没法解决)

案例:

 new Promise(function(resolve,reject){
           resolve(1000)
       }).then(function(res){
            return new Promise(function(resolve,reject){
              throw new Error("错误")
            })
       },function(err){
           console.log(9,err)  //这里是捕获不到的
       })
new Promise(function(resolve,reject){
           resolve(1000)
       }).then(function(res){
            return new Promise(function(resolve,reject){
              throw new Error()
            })
       }).catch(function(error){
           console.log(error) //这里是可以捕获到的。
       })

Promise.all

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

案例:等一和二执行成功后才能执行三

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promise.reject('失败')

Promise.all([p1, p2]).then((result) => {
  console.log(result)  //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)     // 失败了,打出 '失败'
})

Promise.race

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。(谁先完事就走谁)那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})

promise源码

 const PENDING = "PENDING";
        const FULFILLED = "FULFILLED";
        const REJECTED = "REJECTED";
        class MyPromise {
            constructor(fn) {
                this.status = PENDING;
                this.value = undefined;
                this.resovleArr = [];
                this.rejectArr = []
                fn(this.resolve, this.reject);
            }
            resolve = (res) => {

                setTimeout(() => {
                    if (this.status === PENDING) {
                        this.status = FULFILLED;
                        this.value = res;
                        this.resovleArr.forEach(cb => {
                            cb(res)
                        })
                    }
                })
            }
            reject = (res) => {


                if (this.status === PENDING) {
                    this.status = REJECTED
                    this.value = res;
                    this.rejectArr.forEach(cb => {
                        cb(res)
                    })
                }
            }
            then(onFulfilled, onRejected) {
     
                return new MyPromise((resolve, reject) => {

                   
                    onFulfilled = typeof (onFulfilled) == "function" ? onFulfilled : () => { }
                    onRejected = typeof (onRejected) == "function" ? onRejected : () => { }
                   
                    if (this.status === PENDING) {
                        this.resovleArr.push(onFulfilled);
                        this.rejectArr.push(onRejected)
                    }
                    if (this.status === FULFILLED) {
                        setTimeout(() => {
                            onFulfilled(this.value)
                        })

                    }
                    if (this.status === REJECTED) {
                        onRejected(this.value)
                    }
                })

            }
        }

async await(promise的升级版)

await 可以认为是 async wait 的简写

await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西
如果它等到的不是一个Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

image.png fn调用先返回aaa然后返回结束了,然后再返回定时器

module模块化

基本用法

1.export :导出的一定是个完整的变量声明语句,同时导出多个可以 export{ 变量名1,变量名2}(可以导出基本数据类型,数组,对象,函数等)

2.import:引入的时候注意,路径名如果是当前目录需要加 ./不然引入的就是核心模块

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。

导出的同时改名字:

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
};

这是时候其他文件引入的时候

import {streamV1 } from 路径名

import改名

导入的时候再改名

import { lastName as surname } from './profile.js';

PS:

import命令输入的变量都是只读的,因为它的本质是输入接口。

也就是说,不允许在加载模块的脚本里面,改写接口。

export default(默认导出)

导出的是一个值,不是一个声明语句 ,普通导出加大括号,默认导出不加大括号,名字可以自己随便取。默认导出和普通导出可以同时用.一个文件中只能有一个默认导出

//默认导出
export default a;
import 111 from "./a.js"//111是自己随意取名的
//default+值,default充当变量的角色

整个文件的导入

import "./index.css"
import "../index.js"  //此情况被引入的文件无需导出

class类

基本概念

class(类)本质上是构造函数的语法糖,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

function Student(name, age) {
  this.name = name;
  this.age = age;
}

Student.prototype.toString = function () {
  return '(' + this.name + ', ' + this.age + ')';
};

var ll = new Student("李雷", 18);
console.log(ll);

上面的代码用 ES6 的class类改写,就是下面这样。

class Students{
  constructor(name,age){
    this.name=name;
    this.age=age;
  }
}
let ll = new Student("李雷",18);
console.log(ll);

ES6 的类,完全可以看作构造函数的另一种写法。使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致。

类的所有方法都定义在类的prototype属性上面

class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}
// 等同于
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

constructor方法

constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法

class Point {
}

// 等同于
class Point {
  constructor() {}
}

class的继承

Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。es6的继承是直接给一个匿名对象加上属性,再把这个对象赋予子类,继承在前,实例在后

语法:

  class Point {
      fn(){
          console.log(1)
      }
  }
//Point是父类,ColorPoint是子类
//它通过extends关键字,继承了Point类的所有属性和方法
 class ColorPoint extends Point {      
  }  
  let a=new ColorPoint();
  a.fn()

super的用法:

super这个关键字,既可以当作函数使用,也可以当作对象使用。

ES6 规定,子类必须在constructor()方法中调用

原因: super(),否则就会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法。如果不调用super()方法,子类就得不到自己的this对象。

案例:

class Car{
            constructor(color,price,name){
                this.color=color;
                this.price=price;
                this.name=name;
            }
         }
         class Benz extends Car{
            constructor(color,price,name,luxury){
                super(color,price,name)
                this.luxury=luxury;
            }
         }

         class Bmw extends Car{
            constructor(color,price,name,sport){
                super(color,price,name)
                this.sport=sport;
            }
         }

         let c63=new Benz("白色",1000000,"amgc63","豪华");
         let g63=new Benz("黑色",3800000,"amgg63","很豪华");
         let m5=new Bmw("蓝色",1820000,"m5","贼运动");
         console.log(c63);
         console.log(g63);
         console.log(m5);

事件循环

定义:

所有同步任务都在主线程上执行,遇到异步任务时,会存放在任务队列中,当主线程执行之后,会从任务队列中检查代码,拿到主线程中去执行,执行完之后,再去任务队列中取出下一个异步任务,再拿到主线程中去执行,以此反复形成事件循环

案例:

 //这是一个同步任务直接被执行	
  console.log('1')、
  //这是一个宏任务,放进宏任务列表
  setTimeout(function () {
    console.log('2')
  });
  new Promise(function (resolve) {
    console.log('3');//这是同步任务直接被执行
    resolve();
    
  }).then(function () {
        //then是个微任务 ,放进微任务列表
    console.log('4')
    setTimeout(function () {
      console.log('5')
    });
  })
//最终结果为: 1、3、4、2、5