前端面试干货——ES6重点总结

ES6是ECMA为JavaScript制定的第6个标准版本

ES6更新的内容主要分为以下几点

  • 表达式:声明、解构赋值
  • 内置对象:字符串拓展、数值类型拓展、对象拓展、数组拓展、函数拓展、正则拓展、Set、Map、Proxy、Reflect
  • 语句与运算:Class、Module、Iterator
  • 异步编程:Promise、Async、Generator

文章目录

  • 声明 const 和 let
  • 解构赋值
  • 字符串拓展
  • 对象拓展
  • 数组拓展
  • 函数拓展
  • Symbol
  • Set
  • WeakSet
  • Map
  • WeakMap
  • WeakRef
  • Proxy
  • Reflect
  • Promise

声明 const 和 let

const声明常量,let声明变量

作用域:全局、函数、块级

作用范围:var命令在全局代码执行,const和let只能在代码块中执行

重点:

  • 不允许重复声明
  • 未定义使用会报错,const和let不存在变量提升
  • 暂时性死区:在代码块内使用const和let命令声明之前,改变量不可用(必须声明才能用)

解构赋值

  1. 字符串 const [a,b,c,d,e] = "hello"

  2. 数组 const {toString: s } = 123

  3. 布尔 const {toString: s } = true

  4. 数组

    • 规则数据结构具有Iterator接口
    • 直接结构:const [x,y] = [1,2]
    • 赋默认值:const [a='a',b='b'] = ["111","222"]
  5. 对象解构

    const {p,q} = {p:1,q:false}; //基本赋值 p=1 q=false
    
    var a,b;
    ({a,b} = {a:1, b:2}); //对象字面量无声明解构赋值时是必须的。
    
    const { x, y: z } = { x: 1, y: 2 } //给新的变量名赋值 {x:1,z:2}
       ```
    
  6. 函数参数结构

应用场景:交换变量值、返回函数多个值、提取JSON数据

重点:

  • 匹配模式:只要等号两边的模式相同,左边的变量就会被赋予对应的值
  • 解构赋值规则:只要等号右边的值不是对象或数组,就先将其转为对象
  • 解构默认值生效条件:属性值严格等于undefined
  • 解构不成功时变量的值等于undefined
  • undefined和null无法转为对象,因此无法进行解构

字符串拓展

  1. 字符串遍历:可通过for-of遍历字符串

  2. 模板字符串 可以换行,但是所有的空格和换行会被保留。

    const h = 'hello'   
    ` ${ h } word ` 
    
  3. Unicode表示法 //大括号包含表示Unicode字符(\u{0xXX}或\u{0XXX})

  4. 字符串方法拓展:

    String.raw()    //返回把字符串所有变量替换且对斜杠进行转义的结果
    String.fromCodePoint()  //返回码点对应字符
    codePointAt()   //返回字符对应码点(String.fromCodePoint()的逆操作)
    normalize() //把字符的不同表示方法统一为同样形式,返回新字符串(Unicode正规化)
    repeat()    //把字符串重复n次,返回新字符串
    matchAll()  //返回正则表达式在字符串的所有匹配
    includes()  //是否存在指定字符串
    startsWith()    //是否存在字符串头部指定字符串
    endsWith()  //是否存在字符串尾部指定字符串
    
  5. 数值方法拓展

    //二进制表示法0b或0B开头表示二进制(0bXX或0BXX)
    //八进制表示法0o或0O开头表示二进制(0oXX或0OXX)
    Number.EPSILON//数值最小精度
    Number.MIN_SAFE_INTEGER//最小安全数值(-2^53)
    Number.MAX_SAFE_INTEGER//最大安全数值(2^53)
    Number.parseInt()//返回转换值的整数部分
    Number.parseFloat()//返回转换值的浮点数部分
    Number.isFinite()//是否为有限数值
    Number.isNaN()//是否为NaN
    Number.isInteger()//是否为整数
    Number.isSafeInteger()//否在数值安全范围内
    
    //以及各种Math方法
    

对象拓展

  1. 直接写入变量和函数作为对象的属性和方法 {prop, method(){}}
  2. 属性名表达式
    obj.foo = true; // 方法一 直接用标识符作属性名
    obj['a' + 'bc'] = 123; // 方法二 表达式做属性名
    
    // ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名
    //,即把表达式放在方括号内。
    let lastWord = 'last word';
    const a = {
    'first word': 'hello',
    [lastWord]: 'world'
    };
    a['first word'] // "hello"
    a[lastWord] // "world"
    a['last word'] // "world"
    
  3. name属性:函数对象 的name属性返回函数名
    • bind创建的函数 name 属性:bound + 函数名
    • Function构造函数返回的函数实例的 name 属性:anonymous
    • 如果对象的方法是一个Symbol值,那么name属性返回的是这个Symbol值的描述
  4. 属性的可枚举性和可遍历性:Object.getOwnPropertyDescriptor 中enumerable为true则为可枚举,反之则不行
  5. super关键字:指向当前对象的原型对象(只能在对象的简写方法中用)
  6. 增加一些对象方法
    Object.is()//对比两值是否相等
    Object.assign()//合并对象(浅拷贝),返回原对象
    Object.getPrototypeOf()//返回对象的原型对象
    Object.setPrototypeOf()//设置对象的原型对象
    \_\_proto\_\_//返回或设置对象的原型对象
    
  7. 属性的遍历:自身、可继承、可枚举、非枚举、Symbol
     for-in//遍历对象自身可继承可枚举属性
     Object.keys()//返回对象自身可枚举属性键组成的数组
     Object.getOwnPropertyNames()//返回对象自身非Symbol属性键组成的数组
     Object.getOwnPropertySymbols()//返回对象自身Symbol属性键组成的数组
     Reflect.ownKeys()//返回对象自身全部属性键组成的数组
    
    规则:
    • 首先遍历所有数值键,按照数值升序排列
    • 其次遍历所有字符串键,按照加入时间升序排列
    • 最后遍历所有Symbol键,按照加入时间升序排列

数组拓展

  1. 扩展运算符...,内部使用for…of循环。
    转换数组为用逗号分隔的参数序列([…arr],相当于rest/spread参数的逆运算)
    rest参数用于获取函数的多余参数,这样就不需要使用arguments对象了。
    function add(...values) {
    let sum = 0;
    for (var val of values) {
        sum += val;
    }
    return sum;
    }
    add(1, 2, 3) // 6
    
  2. 各种方法
     Array.from()  //转换具有Iterator接口的数据结构为真正数组,返回新数组
     Array.of()  //转换一组值为真正数组,返回新数组
     copyWithin()//指定位置的成员复制到其他位置,返回原数组
     find()  //返回第一个符合条件的成员
     findIndex()  //返回第一个符合条件的成员索引值
     fill()  //根据指定值填充整个数组,返回原数组
     keys()  //返回以索引值为遍历器的对象
     values()  //返回以属性值为遍历器的对象
     entries()  //返回以索引值和属性值为遍历器的对象
    
     //ES6明确将数组空位转为undefined(空位处理规不一,建议避免出现)
    ``
    

重点:
使用keys()、values()、entries()返回的遍历器对象,可用for-of自动遍历或next()手动遍历

函数拓展

  1. 参数默认值:为函数参数指定默认值 Func(x=1,y=2){}
    指定某个参数不得省略,否则抛出err:Func(x=throwMissing()){}
    将参数默认值为undefined,表面此参数可省略Func(undefined,1){}

  2. rest/spread 参数,也就是(…),返回函数的多余参数,以数组形式中存在

  3. name 属性:返回函数的函数名

    • 将匿名函数赋值给变量后调用name属性:空字符串(ES5),变量名(ES6)
    • bind返回的函数:bound + 函数名(ES5 ES6)
    • Function构造函数返回的函数实例:anonymous(ES5 ES6)
  4. 箭头函数

    • 嵌套使用:部署管道机制
    • this指向固定化。箭头函数没有自己的 this ,导致内部的this就是外层代码块的 this ,因此不能用作构造函数
  5. 尾调用优化:在某个函数的最后异步调用另一个函数 return f(x)

箭头函数重点:

  • 函数体内的this是定义时所在的对象而不是使用时所在的对象
  • 可让this指向固定化,这种特性很有利于封装回调函数
  • 不可当作构造函数,因此箭头函数不可使用new命令
  • 不可使用yield命令,因此箭头函数不能用作Generator函数
  • 不可使用Arguments对象,此对象在函数体内不存在(可用rest/spread参数代替)
  • 返回对象时必须在对象外面加上括号

Symbol

定义:独一无二的值
声明:const set = Symbol(‘123’),参数为字符串,可选

方法:

Symbol()//创建以参数作为描述的Symbol值(不登记在全局环境)
Symbol.for()//创建以参数作为描述的Symbol值,如存在此参数则返回原有的Symbol值(先搜索后创建,登记在全局环境)
Symbol.keyFor()//返回已登记的Symbol值的描述(只能返回Symbol.for()的key)
Object.getOwnPropertySymbols()//返回对象中所有用作属性名的Symbol值的数组

注意:

  1. Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。
  2. Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
  3. Symbol 值作为对象属性名时间,不能用点运算符

重点:

  • Symbol值不能与其他类型的值进行运算
  • Symbol值可通过String()或toString()显式转为字符串
  • Symbol值作为对象属性名时,此属性是公开属性,但不是私有属性
  • Symbol值作为对象属性名时,只能用方括号运算符([])读取,不能用点运算符(.)读取
  • Symbol值作为对象属性名时,不会被常规方法遍历得到,可利用此特性为对象定义非私有但又只用于内部的方法

Set

定义:类似于数组的数据结构,成员值都是唯一且没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。

入参:具有Iterator接口的数据结构(可循环、遍历)

属性:
constructor 构造函数,返回Set
size 返回实例成员总数

方法:

  add()   //添加值,返回实例,可以采用链式写法
  delete()   //删除值,返回布尔
  has()   //检查值,返回布尔
  clear()   //清除所有成员
  keys()   //返回键名的遍历器,又因为Set容器没有键名只有键值(或者说键名和键值是同一个值),所以还是返回键值
  values()   //返回以键值为遍历器的对象
  entries()   //返回以属性值和属性值为遍历器的对象
  forEach()   //使用回调函数遍历每个成员

应用:

  1. 去重字符串:[...new Set(str)].join('')
  2. 去重数组:[...new Set([arr])] 或者 Array.from(new Set(arr))
  3. 集合判断:
    • 声明:const a = new Set(arr1)、const b = new Set(arr2)
    • 并集:new Set([...a, ...b])
    • 交集:new Set([...a].filter(v => b.has(v)))
    • 差集:new Set([...a].filter(v => !b.has(v)))
  4. 如果想在遍历操作中同步改变原来的 Set 结构,目前没有直接的方法,但是可以通过原Set映射出一个新的Set然后赋值给原来的 Set 结构;
    // 方法一
    let set = new Set([1, 2, 3]);
    set = new Set([...set].map(val => val * 2));
    // set的值是2, 4, 6
    
    // 方法二
    let set = new Set([1, 2, 3]);
    set = new Set(Array.from(set, val => val * 2));
    // set的值是2, 4, 6
    

重点:

  1. 向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。
  2. 在 Set 内部,两个NaN是相等的。另外,两个对象总是不相等的。
  3. Set的遍历顺序就是插入顺序,但是在对象内部的遍历顺序不是按插入顺序
  4. 由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,

WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,

Map

JS的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

定义:类似于对象的数据结构,成员键是任何类型的值

入参:具有Iterator接口且每个成员都是一个双元素数组的数据结构(Array、Set、Map…)

属性:

  • constructor:构造函数,返回Map
  • size:返回实例成员总数

方法:

get()  //返回键值对
set()  //添加键值对,返回实例,可以采用链式写法
delete()  //删除键值对,返回布尔
has()  //检查键值对,返回布尔
clear()  //清除所有成员
keys()  //返回以键为遍历器的对象
values()  //返回以值为遍历器的对象
entries()  //返回以键和值为遍历器的对象
forEach()  //使用回调函数遍历每个成员

样例:

let map = new Map();

map.set(-0, 123);
map.get(+0) // 123

map.set(true, 1);
map.set('true', 2);
map.get(true) // 1

map.set(undefined, 3);
map.set(null, 4);
map.get(undefined) // 3

map.set(NaN, 123);
map.get(NaN) // 123

重点:

  • 遍历顺序:插入顺序
  • 对同一个键多次赋值,后面的值会将前面的覆盖
  • 对同一个对象的 引用,被视为一个键;对同样值的两个实例引用,视为两个键(键跟内存地址绑定)
  • 添加多个以NaN作为键时,只会存在一个
  • 传统Object提供字符串-值对应,map提供值-值对象

WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。

区别:

  1. WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  2. WeakMap的键名所指向的对象,不计入垃圾回收机制。

基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。

总之,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。

重点:

  1. WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
  2. ES6规定WeakMap结构不可遍历
  3. 即使在外部消除了成员键的引用,内部的成员值依然存在

WeakRef

WeakSet 和 WeakMap 是基于弱引用的数据结构,ES6 更进一步,提供了 WeakRef 对象,用于直接创建对象的弱引用。

let target = {};
let wr = new WeakRef(target);

上面示例中,target是原始对象,构造函数WeakRef()创建了一个基于target的新对象wr。这里,wr就是一个 WeakRef 的实例,属于对target的弱引用,垃圾回收机制不会计入这个引用,也就是说,wr的引用不会妨碍原始对象target被垃圾回收机制清除。

WeakRef 实例对象有一个deref()方法,如果原始对象存在,该方法返回原始对象;如果原始对象已经被垃圾回收机制清除,该方法返回undefined。

Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,相当于重写某部分操作功能

声明:const proxy = new Proxy(target, handler)

入参:

  • target:拦截的目标对象
  • handler:定制拦截的行为,也是一个对象

样例1:

var obj = new Proxy({}, {
  get: function (target, propKey, receiver) {
    console.log(`getting ${propKey}!`);
    return Reflect.get(target, propKey, receiver);
  },
  set: function (target, propKey, value, receiver) {
    console.log(`setting ${propKey}!`);
    return Reflect.set(target, propKey, value, receiver);
  }
});

obj.count = 1
//  setting count!
++obj.count
//  getting count!
//  setting count!
//  2

上面代码说明,Proxy 实际上重载(overload)了点运算符,即用自己的定义覆盖了语言的原始定义

样例2:

var proxy = new Proxy({}, {
  get: function(target, propKey) {
    return 35;
  }
});

proxy.time // 35
proxy.name // 35
proxy.title // 35

上面代码中,配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都得到35。

Proxy 实例也可以作为其他对象的原型对象。

var proxy = new Proxy({}, {
  get: function(target, propKey) {
    return 35;
  }
});

let obj = Object.create(proxy);
obj.time // 35

上面代码中,proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。

拦截方法:

get(target, propKey, receiver)  //拦截对象属性的读取,比如proxy.foo和proxy['foo']。
set(target, propKey, value, receiver)  //拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey)  //拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey)  //拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target)  //拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey)  //拦截Object.getOwnPropertyDescriptor(proxy, propKey)返回属性的描述对象。
defineProperty(target, propKey, propDesc)  //拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target)  //拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target)  //拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target)  //拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto)  //拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, object, args)  //拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args)  //拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

应用场景:

Proxy.revocable() //不允许直接访问对象,必须通过代理访问,一旦访问结束就收回代理权不允许再次访问
get() //读取未知属性报错、读取数组负数索引的值、封装链式操作、生成DOM嵌套节点
set() //数据绑定(Vue数据绑定实现原理)、确保属性值设置符合要求、防止内部属性被外部读写
has() //隐藏内部属性不被发现、排除不符合属性条件的对象
deleteProperty() //保护内部属性不被删除
defineProperty() //阻止属性被外部定义
ownKeys() //保护内部属性不被遍历

重点:

  • 要使Proxy起作用,必须针对实例进行操作,而不是针对目标对象进行操作
  • 没有设置任何拦截时,等同于直接通向原对象
  • 属性被定义为不可读写/扩展/配置/枚举时,使用拦截方法会报错
  • 代理下的目标对象,内部this指向Proxy代理

Reflect

Reflect始终保持Object方法的默认行为,未来的新方法也会放在Reflect对象上

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。

静态方法:

Reflect.apply(target, thisArg, args)   //绑定this后执行指定函数 
Reflect.construct(target, args)     //等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。
Reflect.get(target, name, receiver)         //查找并返回target对象的name属性,如果没有该属性,则返回undefined。
Reflect.set(target, name, value, receiver)  //设置target对象的name属性等于value。
Reflect.defineProperty(target, name, desc)  //该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
Reflect.deleteProperty(target, name)    //等同于delete obj[name],用于删除对象的属性。
Reflect.has(target, name)     //对应name in obj里面的in运算符。第一个参数不是对象,会报错。
Reflect.ownKeys(target) //用于返回对象的所有属性,基本等同于
Reflect.isExtensible(target) //返回一个布尔值,表示当前对象是否可扩展。
Reflect.preventExtensions(target) //用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。
Reflect.getOwnPropertyDescriptor(target, name) //如果指定的属性存在于对象上,则返回其属性描述符对象
Reflect.getPrototypeOf(target) //方法用于读取对象的__proto__属性,不传对象会报错,对应Object.getPrototypeOf(obj)。
Reflect.setPrototypeOf(target, prototype)  //用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)方法。它返回一个布尔值,表示是否设置成功。

设计目的:

  • 将Object中属于语言内部的方法放到Reglect上,并将某些Objec方法报错的情况改为返回 false
  • 让Object的操作变成函数的行为 可以返回信息
  • Proxy 和 Reflect 相辅相成

重点:

  1. 如果name属性部署了读取函数(getter),则读取函数的this绑定receiver。
  2. Proxy方法和Reflect方法一一对应
  3. Proxy和Reflect联合使用,前者负责拦截赋值操作,后者负责完成赋值操作

Promise

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

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象代表一个异步的操作,他的状态不受外界影响,只有结果可以决定返回哪一种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)

Promise的状态一旦发生改变,状态就凝固了称为 resolved 定型,
Promise无法取消,一旦新建他就会立即执行,无法中途取消

出参:

  • resolve:将状态从未完成变为成功,在异步操作成功时调用,并将异步操作的结果作为参数传递出去
  • reject:将状态从未完成变为失败,在异步操作失败时调用,并将异步操作的错误作为参数传递出去

方法:

  1. then():分别指定 resolved 状态和 rejected 状态的回调函数
  2. catch():指定发生错误时的回调函数
  3. Promise.all():将多个实例包装成一个新的实例,返回全部实例状态变更后的结果数据
  4. Promise.race():将多个实例包装成一个新的实例,返回最先发生状态变更的结果
  5. Promise.resolve():将对象转为 resolved 的 Promise对象并返回,等价于new Promise((resolved)=>resolved())
  6. Promise.reject():将对象转为rejected的Promise对象,等价于new Promise((resolved, rejected)=>rejected())

应用场景:加载图片、ajax转Promise对象

重点:

  • 只有异步操作的结果可决定当前状态是哪一种,其他操作都无法改变这个状态
  • 状态改变只有两种可能:从pending变为resolved、从pending变为rejected
  • 一旦新建Promise对象就会立即执行,无法中途取消
  • 不设置回调函数,内部抛错不会反应到外部
  • 当处于pending时,无法得知目前进展到哪一个阶段
  • 实例状态变为resolved或rejected时,会触发then()绑定的回调函数
  • resolve()和reject()的执行总是晚于本轮循环的同步任务
  • then()返回新实例,其后可再调用另一个then()
  • then()运行中抛出错误会被catch()捕获
  • reject()的作用等同于抛出错误
  • 实例状态已变成resolved时,再抛出错误是无效的,不会被捕获,等于没有抛出
  • 实例状态的错误具有冒泡性质,会一直向后传递直到被捕获为止,错误总是会被下一个catch()捕获
  • 不要在then()里定义rejected状态的回调函数(不使用其第二参数)
  • 建议使用catch()捕获错误,不要使用then()第二个参数捕获
  • 没有使用catch()捕获错误,实例抛错不会传递到外层代码,即不会有任何反应
  • 作为参数的实例定义了catch(),一旦被rejected并不会触发Promise.all()的catch()
  • Promise.reject()的参数会原封不动地作为rejected的理由,变成后续方法的参数

你可能感兴趣的:(前端开发必会,前端,es6,javascript)