基本介绍

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

我们从node开始,会大量用到ES6中的一些新语法,因此在学习node之前需要先学习一下es6中提供的新语法

ECMAScript与Javascript的关系

ECMAScript,简称ES,是由Ecma国际(欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)按照标准制定的一种脚本语言规范。

JavaScript是按ECMAScript规范实现的一种脚本语言,JavaScript除了实现了ECMAScript规范,还提供了BOM和DOM的操作。

ECMAScript版本历史

  • ES1.0, 1997年06月发布

  • ES2.0, 1998年06月发布

  • ES3.0, 1999年12月发布

  • ES4.0, 由于关于语言的复杂性出现了分歧。放弃发布

  • ES5.0, 2009年12月发布, 增加了严格模式,增加了少量语法,为ES6铺路

  • ES6.0, 2015年6月发布,增加了大量的新概念和语法特性

    • 第六版的名字, 可以叫做ECMAScript6.0(ES), 也可以叫做ECMAScript 2015(ES2015)
    • ECMA组织决定以后每年6月份都会发布一版新的语法标准,比如ES7(ECMAScript 2016)
    • 通过我们说的ES6泛指ES5之后的下一代标准,涵盖了ES6, ES7, ES8....

ES5

数组的新方法

forEach

forEach() 方法对数组的每个元素执行一次提供的函数。功能等同于for循环.

应用场景:为一些相同的元素,绑定事件处理器!

需求:遍历数组["张飞","关羽","赵云","马超"]

var arr = ["张飞","关羽","赵云","马超"];
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
arr.forEach(function(element, index, array){
  console.log(element, index, array);
});

forEach对比for的好处

1、放置变量的泄露

2、可以结合箭头函数使用arr.forEach(item=>sum+=item)

map

map 映射,会遍历一个数组,将每次函数执行结果,存到型数组中

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

需求:遍历数组,求每一项的平方存在于一个数组中

var arr = [1,2,3,4,5];  // 1 4 9 16 25
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:一个新数组,每个元素都是回调函数的结果。
var newArray = arr.map(function(element, index, array){
  return element * element;
});
console.log(newArray);//[1,4,9,16,25]

filter

filter用于过滤掉“不合格”的元素 如果在回调函数中返回true,那么就留下来,如果返回false,就扔掉

留下的元素会保存到新数组中。

需求:遍历数组,将数组中工资超过5000的值删除[1000, 5000, 20000, 3000, 10000, 800, 1500]

var arr = [1000, 5000, 20000, 3000, 10000, 800, 1500];
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:一个新数组,存储了所有返回true的元素
var newArray = arr.filter(function(element, index, array){
  if(element > 5000) {
    return false;
  }else {
    return true;
  }
});
console.log(newArray);//[1000, 5000, 3000, 800, 1500]

reduce

arr.reduce(callback,[initialValue])

reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。

callback (执行数组中每个值的函数,包含四个参数)

    // 1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
    // 2、currentValue (数组中当前被处理的元素)
    // 3、index (当前元素在数组中的索引)
    // 4、array (调用 reduce 的数组)

initialValue (作为第一次调用 callback 的第一个参数。)

reduce的高级用法

(1)计算数组中每个元素出现的次数

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

let nameNum = names.reduce((pre,cur)=>{
  if(cur in pre){
    pre[cur]++
  }else{
    pre[cur] = 1 
  }
  return pre
},{})
console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

(2)数组去重

let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
    if(!pre.includes(cur)){
      return pre.concat(cur)
    }else{
      return pre
    }
},[])
console.log(newArr);// [1, 2, 3, 4]

(3)将二维数组转化为一维

let arr = [[0, 1], [2, 3], [4, 5]]
let newArr = arr.reduce((pre,cur)=>{
    return pre.concat(cur)
},[])
console.log(newArr); // [0, 1, 2, 3, 4, 5]

(3)将多维数组转化为一维

let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr = function(arr){
   return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
}
console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]

(4)对象里的属性求和

var result = [
    {
        subject: 'math',
        score: 10
    },
    {
        subject: 'chinese',
        score: 20
    },
    {
        subject: 'english',
        score: 30
    }
];

var sum = result.reduce(function(prev, cur) {
    return cur.score + prev;
}, 0);
console.log(sum) //60

some

some用于遍历数组,如果有至少一个满足条件,就返回true,否则返回false。

需求:遍历数组,判断数组是否包含奇数,[2,4,6,8,10,9]

var arr = [2,4,6,8,10,21];
//第一个参数:element,数组的每一项元素
//第二个参数:index,数组的下标
//第三个参数:array,正在遍历的数组
//返回值:布尔类型的值,只要有一个回调函数返回true,就返回true
var flag = arr.some(function(element, index, array){
  console.log(element, index, array);
  if(element %2 == 1){
    return true;
  }else {
    return false;
  }
});
console.log(flag);//true

every

every用于遍历数组,只有当所有的元素返回true,才返回true,否则返回false。

需求:遍历数组,判断数组是否都是偶数,[2,4,6,8,10,9]

  var arr = [2,4,6,8,10,21];
  //第一个参数:element,数组的每一项元素
  //第二个参数:index,数组的下标
  //第三个参数:array,正在遍历的数组
  //返回值:布尔类型的值,只有当所有的元素返回true,才返回true,否则返回false。
  var flag = arr.every(function(element, index, array){
    console.log(element, index, array);
    if(element %2 == 0){
      return true;
    }else {
      return false;
    }
  });
  console.log(flag);//false

ES6

变量

ES6中提供了两个声明变量的关键字:const和let

let的使用

ES6 新增了let命令,用来声明变量。它的用法类似于var

  • let声明的变量只有在当前作用域有效
{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1
  • 不存在变量提升
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
  • 不允许重复声明
let a = 10;
let a = 1;//报错 Identifier 'a' has already been declared
  • 块级作用域(全局作用域,局部作用域)
var age = 20
if(age>180){
    let money = 500
    console.log(money)//外部无法使用money变量
}
console.log(money)//报错,无法找到该变量

const的使用

const声明一个只读的常量。常量:值不可以改变的量

  • const声明的量不可以改变
const PI = 3.1415;
PI = 3; //报错
  • const声明的变量必须赋值
const num;
  • 如果const声明了一个对象,仅仅保证地址不变
const obj = {name:'zs'};
obj.age = 18;//正确
obj = {};//报错
  • 其他用法和let一样
1. 只能在当前代码块中使用
2. 不会提升
3. 不能重复声明

let与const的使用场景

1. 如果声明的变量不需要改变,那么使用const
2. 如果声明的变量需要改变,那么用let
3. 学了const和let之后,尽量别用var
3. 复杂数据类型都需要使用const

解构赋值

可以从对象或者数组中解构出值,赋值给变量。

数组解构

以前,为变量赋值,只能直接指定值。

let a = 1;
let b = 2;
let c = 3;

ES6 允许写成下面这样。

let [a, b, c] = [1, 2, 3];
consoel.log(a,b,c);
//不需要前面的元素可以使用,
let [, b, c] = [1, 2, 3];
consoel.log(b,c);

解构默认值

let [a = 0, b, c] = [1, 2, 3];

对象解构

解构不仅可以用于数组,还可以用于对象。

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"

如果变量名与属性名不一致,可以使用:

定义变量与对象中属性不一样的名字

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

解构时需要指定默认值,可以使用=

默认值:是值对象中没有该属性的值,使用默认值,如果有值使用对象中的属性值

const obj = {
    name:"zs",
    age:18
}
const {name,age,desc = "不错"}=obj

函数参数的解构

函数的参数也可以使用解构赋值。

function fn({name,age},money){
  console.log(name,age,money)
}
const obj ={
    name:"zs",
    age:18
}
fn(obj,100)

字符串

模版字符串

传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。

$('#result').append(
  'There are <b>' + basket.count + '</b> ' +
  'items in your basket, ' +
  '<em>' + basket.onSale +
  '</em> are on sale!'
);

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。反引号:``

$('#result').append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

字符串模版的优点

  • 允许换行
  • 可以使用插值 ${变量名}

字符串方法

es5新增方法

  • **includes()**:返回布尔值,表示是否找到了参数字符串。
  • **startsWith()**:返回布尔值,表示参数字符串是否在原字符串的头部。
  • **endsWith()**:返回布尔值,表示参数字符串是否在原字符串的尾部。

数组

find

find是ES6新增的语法

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined

// 获取第一个大于10的数
var array1 = [5, 12, 8, 130, 44];

var found = array1.find(function(element) {
  return element > 10;
});
console.log(found);

findIndex

findIndex是ES6新增的语法

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。

// 获取第一个大于10的下标
var array1 = [5, 12, 8, 130, 44];

function findFirstLargeNumber(element) {
  return element > 13;
}

console.log(array1.findIndex(findFirstLargeNumber));

函数-箭头函数

ES6标准新增了一种新的函数:Arrow Function(箭头函数)。

为什么叫Arrow Function?因为它的定义用的就是一个箭头:

基本使用

var fn = function(x, y) {
    console.log(x + y);
}

相当于
//语法: (参数列表) => {函数体}
var fn = (x, y) => {
    console.log(x + y);
}

参数详解

  • 如果没有参数列表,使用()表示参数列表
var sum = () => {
    console.log('哈哈')
};
// 等同于:
var sum = function() {    
    console.log('哈哈')
};
  • 如果只有一个参数,可以省略()
// 等同于:
var sum = function(n1) {    
    console.log('哈哈')
};

var sum = n1 => {
    console.log('哈哈')
};
  • 如果有多个参数,需要使用()把参数列表括起来
var sum = function(n1, n2) {    
    console.log('哈哈')
};

var sum = (n1, n2) => {
    console.log('哈哈')
};

返回值详解

  • 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来
var sum = function(n1) {    
    console.log('哈哈')
};

var sum = n1 => {
    console.log('哈哈')
};
  • 如果函数体只有一行一句,那么可以省略{}和return
var fn = function(n1, n2) {
    return n1 + n2;
}

var fn = (n1, n2) => n1 + n2;

案例

  1. 有一个数组[1,3,5,7,9,2,4,6,8,10],请对数组进行排序
  2. 有一个数组['a','ccc','bb','dddd'],请按照字符串长度对数组进行排序
  3. 有一个数组,[57,88,99,100,33,77],请保留60分以上的成绩,返回一个新的数组

箭头函数的注意点

this指向

  1. 箭头函数内部没有this,因此箭头函数内部的this指向了外部的this
  2. 箭头函数不能作为构造函数和事件绑定,因为箭头函数没有this
  3. 定时器和延时器一律使用箭头函数简化

【定义一个对象,定时器打招呼】

苦口婆心一下:箭头函数刚开始用,肯定会有点不习惯,但是任何东西都有一个习惯的过程,慢慢接受就好了,多用,多练

函数参数

默认参数

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

rest参数(剩余参数)

function add(...values) {

}
//收集所有没有使用的参数,是一个数组
add(2, 5, 3) // 10

对象

简写

属性简写

const user = 'zs'
const age = 18
const obj = {
    name: username,
    age
}

方法简写

const obj = {
    name: username,
    age,
    sayHi(){
        console.log("hi")
    }
}

展开运算符

数组

展开运算符可以展开一个数组或者对象

const arr = [1,2,3,4,5]
console.log(...arr)=>console.log(1,2,3,4,5)

展开一个数组,用于数组拼接

const arr1 = [1,2,3]
const arr2 = [4,5,6]
const newArr=[...arr1,..arr2]

求数组最大值

const arr=[1,2,3]
const max=Math.max(...arr)

对象

展开一个对象,用于对象拼接

const obj1={
    car:"老师来说"
}
const obj2={
    name:"牛牛"
}
const newObj={
    ...obj1,
    ...obj2
}

属性的遍历

for...in

for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

// 遍历一个对象
const obj = {
    id: '1',
    name: 'lisi',
    phone: '1835257431'
}

for(const key in obj){
    key // 对象的属性名
    obj[key] // 对象的属性值
    
    console.log(key,obj[key])
}

Object.keys()

返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

// 判断是否是空对象
const obj = {}
const arr = Object.keys(obj)
arr.length > 0 ? true : false

Object.keys(obj)

Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

  • 首先遍历所有数值键,按照数值升序排列。
  • 其次遍历所有字符串键,按照加入时间升序排列。
  • 最后遍历所有 Symbol 键,按照加入时间升序排列。

set集合

用于数组去重

const arr = [1,2,2,3,4,4,5,99,99]
//去除重复
const s = new Set(arr)
//将重复项转换为数组
const arr = [...s]

flat方法

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

示例

扁平化嵌套数组

var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

扁平化与数组空项

var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]111

class类

传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。

ES6引入了Class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言。

class定义类

// 使用class创建构造函数
class Point {
    constructor (x,y){
        this.x = x
        this.y = y
    }
    getPostion(){
       return `${this.x},${this.y}`
    }
}

// 使用new创建实例
const p1 = new Point(1,2)

constructor方法是类的构造函数的默认方法,通过new命令生成对象实例时,自动调用该方法。constructor方法如果没有显式定义,会隐式生成一个constructor方法。所以即使你没有添加构造函数,构造函数也是存在的。constructor方法默认返回实例对象this,但是也可以指定constructor方法返回一个全新的对象,让返回的实例对象不是该类的实例。

constructor中定义的属性可以称为实例属性(即定义在this对象上),constructor外声明的属性都是定义在原型上的,可以称为原型属性(即定义在class上)。hasOwnProperty()函数用于判断属性是否是实例属性。其结果是一个布尔值, true说明是实例属性,false说明不是实例属性。in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

set/get存取值函数

var info = {
    _age:18,
    // 设置值的同名函数
    set age (newValue){
        if (newVlaue>18){
            console.log('怎么变老了')
        } else {
            console.log('哈哈')
        }
    },
    // 取值的同名函数
    get age () {
        console.log('你问我年龄干嘛')
        return this._age
    }
}
console.log(info.age)

//-------------------------------------------------

class info = {
    constructor (age){
        this._age = age
    }
    // 设置值的同名函数
    set age (newAge){
        if (newAge>18){
            console.log('new age is:' + newAge )
        } else {
            console.log('哈哈')
        }
    },
    // 取值的同名函数
    get age () {
        return this._age
    }
}
const info = new Info (18)
console.log(info)

使用class定义表达式

// es5语法定义函数
const func = function () {}
function func () {}
// 使用es6语法class定义函数
class Infos {
    constructor () {}
}
const Infos = class {
    constructor () {}
}

设置静态方法和属性

// class 设置 静态方法 // es6 只有静态方法没有静态属性
class Point {
    constructor (x,y) {
        this.x = x;
        this.y = y
    }
    getPosition () {
        return `${this.x},${this.y}`
    }
    // 定义静态方法
    static getClassName () {
        return Point.name
    }
}
const p = new Point(1,2)
console.log(p.getClassName) // 结果显示not is function

// 使用特殊方式实现静态属性
class Point {
    constructor (){
        this.x = 0
    }
}
Point.y = 2
const p = new Point () // 这里的p无法继承构造函数中y的值

设置私有方法

// 设置私有方法特殊方式 // es6中没有提供私有方法和私有属性
// 1、
const _func2 = () => {}
class Point {
    func1 () {
        _func2.call(this)
    }
}
// 2、
    // a.js
const func1 = Symbol('func1')
export default class Point {
    static [func1] () {
        
    }
}
    // b.js
import Point from 'a.js'
const p = new Point()
console.log(p) // 可以显示该方法,但是没有Symbol值无法调用

设置私有属性

// 设置私有属性的特殊方法
class Point {
    //现在只是提案还未发布
    #ownProp = 12
}

new.target属性

function Point() {
    console.log(new.target)
}
const p = new Point() // 调用 new.target 可以用来判断是否是用new创建的实例

类的继承

简单示例

// 父类
class Parent {
    constructor () {
        console.log(new.target)
    }
}
// 子类 
class Child extends Parent {
    constructor () {
        super
    }
}

// 使用Child类创建实例
const c  = new Child() // 会检测到继承到的new.target属性,被输出出来

es7

Array.prototype.includes()

includes()作用,是查找一个值在不在数组里,若是存在则返回true,不存在返回false.

1.基本用法:

['a', 'b', 'c'].includes('a')     // true
['a', 'b', 'c'].includes('d')     // false

3.与ES6中的indexOf()比较

  • 简便性

includes()返回的是布尔值,能直接判断数组中存不存在这个值,而indexOf()返回的是索引,这一点上前者更加方便。

  • 精确性

    两者都是采用===的操作符来作比较的,不同之处在于:对于NaN的处理结果不同。

    我们知道js中 NaN === NaN 的结果是false,indexOf()也是这样处理的,但是includes()不是这样的。

['a', 'b', 'c'].includes('a')          // true
['a', 'b', 'c'].indexOf('a') > -1      // true

var arr = [1, 2, 3]
var a = 1;
arr.includes(a)   // true
arr.indexOf(a)    // 0 

注意

// 在判断 +0 与 -0 时,被认为是相同的。
[1, +0, 3, 4].includes(-0)    //true
[1, +0, 3, 4].indexOf(-0)     //1

// 只能判断简单类型的数据,对于复杂类型的数据,比如对象类型的数组,二维数组,这些,是无法判断的.
var arr = [1, [2, 3], 4]
arr.includes([2, 3])   //false
arr.indexOf([2, 3])    //-1

求幂运算符

基本用法:

3 ** 2  //9
效果同
Math.pow(3, 2) //9

由于是运算符,所以可以和 +=一样的用法

var b = 3;
b **= 2;
console.log(b); //9

es8

promise

异步与回调函数的说明

回调函数:

  1. 把一个函数当成参数传递, 将来特定的时机调用, 这个函数就叫回调函数

  2. 什么时候会用到回调函数, 异步的时候 (定时器, 延时器, ajax success 和 error)

    console.log(1)
    
    setTimeout(function() {
        console.log(2)
        
        setTimeout(function() {
            console.log(4)
        }, 2000)
        
        console.log(5)
        
    }, 2000)
    
    console.log(3)
    

回调函数的问题:

  1. 回调函数的阅读性不好, 回调不会立马执行
  2. 回调函数如果大量的嵌套, 可维护性差 (回调地狱)

回调函数的嵌套问题

按照顺序依次读取 - a, b, c, d 四个文件

回调地狱: 回调函数嵌套回调函数, 嵌套多了, 将来就很难维护, 很难理清顺序

promise 的基本语法

目的: promise 是书写异步代码的另一种方式, 解决回调函数嵌套的问题

  1. 如何创建一个 promise 对象

    const p = new Promise((resolve, reject) => {
        promise内部会封装一个异步操作
        成功调用 resolve
        失败调用 reject
    })
    
  2. 如何使用一个 promise 对象

    2. 使用 promise 对象
      .then(res => { ... }) 处理成功
      .catch(res => { ... }) 处理失败
    

promise 解决回调地狱的问题

如果有多个 promise 需要处理, 支持链式编程

const p = new Promise(function (resolve, reject) {
  // promise 内部会封装一个异步的操作
  // resolve: 成功的时候, 需要调用
  // reject: 失败的时候, 需要调用
  fs.readFile('a.txt', 'utf8', (err, data) => {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})
const p2 = new Promise(function (resolve, reject) {
  fs.readFile('b.txt', 'utf8', (err, data) => {
    if (err) {
      reject(err)
    } else {
      resolve(data)
    }
  })
})
....

p.then(res => {
  console.log(res)
  return p2
}).then(res => {
  console.log(res)
  return p3
}).then(res => {
  console.log(res)
  return p4
}).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

基本用法

function read (filename) {
  return new Promise(function (resolve, reject) {
    // promise 内部会封装一个异步的操作
    // resolve: 成功的时候, 需要调用
    // reject: 失败的时候, 需要调用
    fs.readFile(filename, 'utf8', (err, data) => {
      if (err) {
        reject(err)
      } else {
        resolve(data)
      }
    })
  })
}

read('a.txt').then(res => {
  console.log(res)
  return read('b.txt')
}).then(res => {
  console.log(res)
  return read('c.txt')
}).then(res => {
  console.log(res)
  return read('d.txt')
}).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

Promise常用的几个方法说明

  • Promise.resolve()

    • 用法:Promise.resolve( 成功的数据 )
    • 直接得到一个成功的 Promise
  • Promise.reject()

    • 用法:Promise.reject( 失败的原因 )
    • 直接得到一个失败的 Promise
  • Promise.all([promise1, promise2, promise3, ...])

    • 作用:all 方法会在所有的 promise 参数全部成功之后,再执行 .then
Promise.all([
  axios.get('接口1'),
  axios.get('接口2'),
  axios.get('接口3'),
]).then(res => {
  // 当此处的代码执行的时候, 三个 axios 请求,已经全部完成了
  // 此时, res => [ 接口1的结果, 接口2的结果, 接口3的结果 ]
})
  • Promise.race([promise1, promise2, promise3, ...]) - 竞速
    • 作用: 只要有一个 promise 完成了, 就会执行 .then 方法
Promise.race([
  axios.get('接口1'),
  axios.get('接口2'),
  axios.get('接口3'),
]).then(res => {
  // 只要一个 axios 接口返回数据了,就会执行此处的代码
  // 此时, res => 表示最先完成的 axios 的结果
})

async 和 await

async 和 await 解决回调问题

async 和 await 是一对关键字

  1. async用于修饰一个函数, 表示一个函数是异步的

  2. await 用于等待一个成功结果, 只能用在 async 函数中

  3. await 后面一般会跟一个promise对象, await会阻塞async函数的执行, 直到等到了 promise成功的结果(resolve的结果)

  4. await 只会等待 promise 成功的结果, 如果失败了会报错, 需要 try catch

使用 async 和 await 去优化代码

  1. 修改状态的优化
  2. 删除的$confirm 和删除ajax功能的优化
  3. 获取功能的优化
  4. 登录: 表单校验 和 登录ajax请求优化