ES6 新特性学习

概览

ES6,全称ECMAScript 6.0,是JavaScript的下一版本标准,2015.06发布。

ES6主要是为了解决ES5的先天不足,比如JavaScript里没有类的概念、内置集合对象Map和Set、迭代器、异步编程等。

特性

1 声明与表达式

1.1 let 与 const

let 和 const 是在ES6中新增的两个关键字。

let声明的变量只在let命令所在的代码块内有效,不能重复声明, 不存在变量提升。

var 定义的变量是在全局范围内有效,可以重复声明,存在变量提升。

{
    console.log(a)   // ReferenceError: Cannot access 'a' before initialization
    let a = 0 
    console.log(a)   // 0
    let a = 1  // SyntaxError: Identifier 'a' has already been declared
}
console.log(a)  // ReferenceError: a is not defined

const声明一个只读的常量,一旦声明,常量的值就不能改变。意味着一旦声明就必须初始化,否则会报错。

const PI = "3.1415926";
console.log(PI)  // 3.1415926
const MY_AGE;  // SyntaxError: Missing initializer in const declaration    

const 保证的不是变量的值不变,而是保证变量所指向的内存地址所保存的数据不允许改动。

1.2 解构赋值

解构赋值是对赋值运算符的扩展,是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段的获取。

数组模型解构(Array)
  • 基本
let [a,b,c] = [1,2,3]
console.log(a)  // 1
console.log(b)  // 2
console.log(c)  // 3
  • 可嵌套
let [a,[[b],c]] = [1,[[2],3]]
  • 可忽略
let [a, ,b] = [1,2,3]
  • 不完全解构
let [a = 1, b] = []
console.log(a)  // a = 1
console.log(b)  // b = undefined
  • 剩余运算符
let [a, ...b] = [1,2,3]
console.log(a)  // a = 1
console.log(b)  // b = [2,3]
  • 字符串等
let [a,b,c,d,e] = 'hello'
console.log(a)  // a = 'h'
console.log(b)  // a = 'e'
console.log(c)  // a = 'l'
console.log(d)  // a = 'l'
console.log(e)  // a = 'o'
  • 解构默认值
let [a = 2] = [undefined]
console.log(a)  // a = 2

当解构模式有匹配结果,且匹配结果是undefined时,会触发默认值作为返回值

let [a = 3, b = a] = [];     // a = 3, b = 3
let [a = 3, b = a] = [1];    // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
对象模型解构(Object)
  • 基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }
console.log(foo)  //  foo = 'aaa'
console.log(bar)  //  bar = 'bbb'

其它可参考数组模型的解构

1.3 Symbol

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。

  • 基本用法
let sy = Symbol('kk')
console.log(sy)    // Symbol(kk)
typeof(sy)         // 'symbol'

//相同参数 Symbol() 返回的值不相等
let sy1 = Symbol('kk')
sy === sy1        // false
  • 注意事项
  • Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
  • Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问
  • 不会出现在 for...in 、 for...of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回
  • 要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到
  • Symbol.for()

类似单例模式,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。

let yellow = Symbol("Yellow");
let yellow1 = Symbol.for("Yellow");
yellow === yellow1;      // false
 
let yellow2 = Symbol.for("Yellow");
yellow1 === yellow2;     // true
  • Symbol.keyFor()

Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。

let yellow1 = Symbol.for("Yellow");
Symbol.keyFor(yellow1);    // "Yellow"

2 内置对象

2.1 Map 与 Set

Map

Map 对象保存键值对。任何值(对象或者原始值)都可以作为一个键或者一个值。

Maps 和 Objects 的区别:

  • 一个Object的键只能是字符串或者是Symbol, 但一个Map的键可以是任意值。
  • Map中的键值是有序的(FIFO原则),而添加到对象中的键值则不是。
  • Map中的键值对个数可以从size属性获取,而Object的键值对个数只能手动计算。
  • Object都有自己的原型,原型链上的键名有可能和你自己在对象上设置的键名产生冲突。
  • 基本使用

    let myMap = new Map()
    myMap.set('key','value')   // key is string
    myMap.get('key')           // value
    myMap.set(NaN, 'not a number')  // key is NaN
    
  • Map的迭代

    for ... of

    let myMap = new Map()
    myMap.set(0, 'zero')
    myMap.set(1, 'one')
    
    for(let [key,value] of myMap){
        console.log(key + ' = ' + value)
    }
    

    forEach()

    myMap.forEach(function(key,value){
        console.log(key + ' = ' + value)
    })
    
  • Map对象的操作

    Map 与 Array 的转换

    let vkArr = [['key1','value1'],['key2','value2']]
    let myMap = new Map(vkArr)      // 将一个二维数组转换成 Map 对象
    let outArr = Array.from(myMap)  // 将一个Map 转换成 Array
    

    Map 的克隆

    let originalMap = new Map([['key1','value1'],['key2','value2']])
    let cloneMap = new Map(originalMap)
    console.log(originalMap === cloneMap)  // false, Map对象构造函数生成实例,迭代出新的对象
    

    Map 的合并

    let first = new Map([[1, 'one'], [2, 'two'], [3, 'three'],])
    let second = new Map([[1, 'uno'], [2, 'dos']])
    let merged = new Map(...first, ...second)  //合并Map对象时,如果键值重复,则后面的覆盖前面的
    
Set

Set对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

Set对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要注意:

  • +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;
  • undefined 与 undefined 是恒等的,所以不重复;
  • NaN 与 NaN 是不恒等的,但是在Set中只能存一个,不重复;
  • 基本使用

    let mySet = new Set()
    mySet.add(1)
    mySet.add(5)
    mySet.add('string')
    
  • 类型转换

    let mySet = new Set(['value1','value2','value3'])     // Array 转 Set
    let myArray = [...mySet]    // 用 ...操作符 将Set 转 Array 
    //string 转 set
    let mySet = new Set('hello')  // Set(4) {'h','e','l','l','o'}
    
  • Set 对象作用

    //数组去重
    let mySet = new Set([1,2,3,4,4])
    [...mySet]  //[1,2,3,4]
    //并集
    let a = new Set([1,2,3])
    let b = new Set([4,3,2])
    let union = new Set([...a, ...b]) // Set(4) {1,2,3,4}
    //交集
    let intersect = new Set([...a].filter(x => b.has(x)))  // Set(2) {2,3}
    //差集
    let difference = new Set([...a].filter(x => !b.has(x)))  // {1}
    

2.2 Proxy 与 Reflect

Proxy(代理)

Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些额外的操作。‘

  • 基本用法

    let target = {
        name: 'Tom',
        age: 24
    }
    let handler = {
        get: function(target, key) {
            console.log('getting ' + key);
            return target[key]; // 不是target.key
        },
        set: function(target, key, value) {
            console.log('setting ' + key);
            target[key] = value;
        }
    }
    let proxy = new Proxy(target, handler)
    proxy.name     // 实际执行 handler.get
    proxy.age = 25 // 实际执行 handler.set
    
Reflect(映射)

Reflect 可以用于获取目标对象的行为,与Object类似,但是更易读,为操作对象提供了一个种更优雅的方式。它的方法与Proxy是对应的。

let exam = {
    name: "Tom",
    age: 24,
    get info(){
        return this.name + this.age;
    }
}
Reflect.get(exam, 'name'); // "Tom"
Reflect.set(exam, 'age', 25); // true
组合使用

Reflect 对象的方法与Proxy对象的方法是一一对应的,所以Proxy对象的方法可以通过调用Reflect对象的方法获取默认行为,然后进行额外操作。

    let target = {
        name: 'Tom',
        age: 24
    }
    let handler = {
        get: function (target, key) {
            console.log('getting ' + key);
            // return target[key];
            return Reflect.get(target,key);
        },
        set: function (target, key, value) {
            console.log('setting ' + key + ' value: ' + value);
            // target[key] = value;
            Reflect.set(target,key,value);
        }
    }
    let proxy = new Proxy(target,handler)
    proxy.age = 33
    console.log('name is '+ proxy.name)

2.3 字符串

扩展的方法
方法名 方法描述 示例
includes() 返回boolean,判断是否找到参数字符串 'hello'.includes('llo') // true
startsWith() 返回boolean,判断参数字符串是否在原字符串的头部 'hello'.startsWith('he') // true
endsWith() 返回boolean,判断参数字符串是否在原字符串的尾部 'hello'.endsWith('o') //true
indexOf() 返回字串的位置 'hello'.indexOf('h') // 0
lastIndexOf() 返回字串最后出现的位置 'hello'.lastIndexOf('h') // 4
repeat() 返回新字符串,表示将字符串重复指定次数 'hello,'.repeat(2) // 'hello,hello,'
padStart() 返回新字符串,用参数字符从头部(左侧)补全字符串 'h'.padStart(5,'o') // 'ooooh'
padEnd() 返回新字符串,用参数字符从尾部(右侧)补全字符串 'h'.padEnd(5,'o') // 'hoooo'
模板字符串

模板字符串相当于加强版的字符串,用反引号`,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。

let name = 'Mike'
let age = 24
let info = `My name is ${name}, I am ${age + 1} years old next year.`

2.4 数值

N/A

2.5 对象

对象字面量
  • 属性的简洁表示法。允许对象的属性直接写变量,这时候属性名就是变量名,属性值就是变量值。

    const age = 12
    const name = 'Amy'
    const person = {age, name}
    console.log(person)  // {age: 12, name: 'Amy'}
    
  • 方法名也可以简写

    const person = {
        sayHi(){ console.log('Hi')}
    }
    //等同于
    const person = {
        sayHi: function(){ console.log('Hi')}
    }
    
  • 属性名表达式

    const obj = {
        ['hel' + 'lo']() { return 'Hi'}
    }
    obj.hello()
    

    注意:属性的简洁表示法和属性名表达式不能同时使用,否则会报错

对象的拓展运算符

拓展运算符 (...) 用于取出参数对象所有可遍历属性然后拷贝到当前对象

  • 基本用法

    let person = {name: 'Amy', age: 12}
    let someone = {...person}
    console.log(someone)  // {name: 'Amy', age: 12}
    
  • 用于合并两个对象

    let age = {age: 12}
    let name = {name: 'Amy'}
    let person = {...age, ...name}
    

    自定义的属性和拓展运算符对象里面属性相同的时候,自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉。

对象的新方法
  • Object.assign(target, source, ...)

    用于将源对象的所有可枚举属性复制到目标对象中。

    let target = {a: 1}
    let obj1 = {b: 2}
    let obj2 = {c: 3}
    Object.assign(target,obj1, obj2)
    console.log(target)  // {a:1, b:2, c:3}
    

    a. 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。

    b. 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象后返回。

    c. assign 的属性拷贝是浅拷贝

  • Object.is(value1, value2)

    用于比较两个值是否严格相等,与 (===) 基本类似。

    Object.is('q','q')   // true
    Object.is([1],[1])   // false 对象类型要同时比较地址和值
    Object.is({q: 1},{q: 1})  //false 对象类型要同时比较地址和值
    

    与 (===) 的区别

    // 1. +0 不等于 -0
    Object.is(+0, -1)   // false
    +0  === -0          // true
    // 2. NaN 等于本身
    Object.is(NaN, NaN)  // true
    NaN === NaN          // false
    

2.6 数组

数组的创建
  • Array.of()

    将参数中所有值作为元素形成数组。

    console.log(Array.of(1,2,3,4))   // [1,2,3,4]
    //参数值可为不同类型
    console.log(Array.of(1,'2',true))  // [1, '2', true]
    //参数为空时返回空数组
    console.log(Array.of())  // []
    
  • Array.from()

    将类数组对象或可迭代对象转化为数组

    // 参数为数组,返回与原数组一样的数组
    console.log(Arrays.from([1,2]))   // [1, 2]
    //参数含空位
    console.log(Arrays.from([1, ,3])) // [1, undefined, 3]
    
  • 类数组

    一个类数组对象必须含有 length 属性,且元素属性名必须是数组或可转换为数值的字符。

    let arr = Array.from({
        0: '1',
        1: '2',
        2: '3',
        length: 3
    })
    console.log(arr)    //['1','2','3']
    
  • 转换可迭代对象

    // 转换 map
    // 转换 set
    // 转换 string
    let arr = Array.from(map | set | string)
    
扩展的方法
方法名 描述 示例
find() 查找数组中符合条件的元素,若有多个,返回第一个元素 Array.from(1,2,3).find(x=>x>2) // 3
findIndex() 查找数组中符合条件的元素的索引,若有多个,返回第一个 Array.from(1,2,3).findIndex(x=>x>2) // 2
fill() 将一定范围索引的数组元素填充为单个指定的值 Array.from(1,2,3,4).fill(0,1,2) // [1,0,3,4]
copyWithin() 将一定范围索引的数组元素修改为此数组另一指定范围索引的元素 Array.from(1,2,3,4).copyWithin(0,2,4) // [3,4,3,4]
entries() 遍历键值对 N/A
keys() 遍历键名 N/A
values() 遍历键值 N/A
includes() 数组是否包含指定值 N/A
flat() 嵌套数组转一维数组 [1,[2,3]].flat() // [1,2,3]
flatMap() 先对数组中每个元素进行了处理,再对数组执行flat() [1,[2,3]].flatMap(x=>x*2) // [2,4,6]
数组缓冲区

数组缓冲区是内存中的一段地址。实际字节数在创建时确定,之后只可修改其中的数据,不可修改大小。

let buffer = ArrayBuffer(10)
console.log(buffer.byteLength)   // 10
  • 视图

    视图是用来操作内存的接口。视图可以操作数组缓冲区或缓冲区字节的子集,并按照其中一种数值数据类型来读取和写入数据。

    DataView 类型是一种通用的数组缓冲区视图,其支持所有8种数值型数据类型。

    //默认DataView可操作缓冲区的全部内容
    let buffer = new ArrayBuffer(10)
    let dataView = new DataView(buffer)
    dataView.setInt8(0,1)
    console.log(dataView.getInt8(0))   //1
    //通过设置偏移量和长度指定DataView可操作的字节范围
    let dataView1 = new DataView(buffer, 0, 3)
    dataView1.setInt8(5,1)  // RangeError
    
定型数组

数组缓冲区特定类型的视图。可以强制使用特定的数据类型,而不是使用通用的DataView对象来操作数组缓冲区。

let buffer = new ArrayBuffer(10)
let view = new Int8Array(buffer)
console.log(view.byteLength)   // 10

length 属性不可写,如果尝试修改这个值,在非严格模式下会直接忽略该操作,在严格模式下会抛出错误。

扩展运算符
  • 复制数组
let arr = [1,2],
    arr1 = [...arr]
console.log(arr1)   //[1,2]
//合并数组
console.log(...[1,2],...[3,4])     // [1,2,3,4]

3 运算符与语句

3.1 函数

  • 函数参数的扩展

    默认参数

    function fn(name, age = 17){ console.log(name + ', ' + age)}
    fn('Amy', 18)  // Amy, 18
    fn('Amy')      // Amy, 17
    

    a. 使用函数的默认参数时,不允许有同名参数。

    b. 只有在未传递参数,或者参数为undefined时,才会使用默认参数,null值被认为是有效的值传递。

    c. 函数参数默认值存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其它参数的默认值。

    不定参数

    不定参数用来表示不确定的参数个数,形如, ...变量名,由...加上一个具名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。

    function fn(...values){ console.log(values.length)}
    fn(1, 2)      // 2
    fn(1,2,3,4)   // 4
    
  • 箭头函数

    箭头函数提供了一种更加简洁的函数书写方式。

    let f = v => v
    // 等价于
    let f = function(v){ console.log(v)}
    f(1)   // 1
    

    当箭头函数没有参数或者有多个参数时,要用()括起来。

    当箭头函数有多行语句,用{} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略{} 结果自动返回。

    let f = (a,b) => {
        let result = a + b
        return result
    }
    f(6,2)   // 8
    

    当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来

    let f = (id,name) = ({ id: id, name: name})
    f(6,2)  // {id: 6, name: 2}
    

    没有 this, super, arguments和new.target 绑定。

    箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时对象。

  • 适用场景

    N/A

  • 不适用场景

    N/A

3.2 class 类

class(类)作为对象的模板被引入,可以通过class关键字定义类。其实class的本质就是function,它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

// 匿名类
let Example = class {
    constructor(a){
        this.a = a
    }
}
// 命名类
let Example = class Example{
    constructor(a){
        this.a = a
    }
}

a. 类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。

b. 类中方法不需要 function 关键字

c. 方法间不能加分号。

  • 类的主体 (以下属性、方法、实例化,与其它任一编程语言都是相通的,不再作解释)

    属性

    静态属性

    公共属性

    实例属性

    name属性

    方法

    constructor方法

    静态方法

    原型方法
    
    实例方法
    

    类的实例化

    new

    实例化对象
    
  • decorator

    decorator 是一个函数,用来修饰类的行为,在代码编译时产生作用。

    类修饰

    function testable(target){
        target.isTestable = true
    }
    
    @testable
    class Example{}
    Example.isTestable    // true
    

    上面的例子添加的是静态属性,若要添加实例属性,在类的 prototype 上操作即可。

    方法修饰

    class Example{
        @writable
        sum(a, b) {return a + b}
    }
    function writable(target, name, descriptor){
        descriptor.writable = false
        return descriptor
    }
    

    修饰器执行顺序: 由外向内进入,由内向外执行。

  • 封装与继承

    getter / setter

    class Example{
        constructor(a, b) {
            this.a = a; // 实例化时调用 set 方法
            this.b = b;
        }
        get a(){
            console.log('getter');
            return this.a;
        }
        set a(a){
            console.log('setter');
            this.a = a; // 自身递归调用
        }
    }
    let exam = new Example(1,2); // 不断输出 setter ,最终导致 RangeError
    

    extends

    class Child extends Father{ ... }
    

    Super

    子类constructor方法中必须有super,且必须出现在this之前

    class Father {
        constructor() {}
    }
    class Child extends Father {
        constructor() {}
        // or 
        // constructor(a) {
            // this.a = a;
            // super();
        // }
    }
    let test = new Child();
    

    不可继承常规对象

    var Father = {
        // ...
    }
    class Child extends Father{
        // ...
    }
    // Uncaught TypeError: Class extends value #<Object> is not a constructor or null
    

3.3 ES6 模块

ES6引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。

ES6的模块分为导出(export) @与导入(import) 两个模块

  • 特点

    a. ES6 的模块自动开启严格模式,不管你有没有在模块头部加上 use strict。

    b. 模块中可以导入和导出各种类型的变量,如函数,对象,字符串,数字,布尔值,类等。

    c. 每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域。

    d. 每一个模块只加载一次(单例),若再去加载同目录下的同文件,直接从内存中读取。

  • export 与 import

    模块导入导出各种类型的变量,如字符串,数值,函数,类。

    a. 导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。

    b. 不仅能导出声明还能导出引用(函数)。

    c. export 命令可以出现在模块的任何位置,但必须处于模块顶层。

    d.import 命令会提升到整个模块的头部,首先执行。

import 命令的特点:

  • 只读属性

  • 单例模式

export default 命令:

a. 在一个文件或模块中,export、import 可以有多个,export default 仅有一个。

b. export default 中的 default 是对应的导出接口变量。

c. 通过 export 方式导出,在导入时要加{ },export default 则不需要。

d. export default 向外暴露的成员,可以使用任意变量来接收。
  • 复合使用

    export 与 import 可以在同一模块中使用,使用特点:

    a. 可以将导出接口改名,包括default。

    b. 复合使用 export 与 import ,也可以导出全部,当前模块导出的接口会覆盖继承导出的。

4 异步编程

4.1 Promise 对象

Promise是异步编程的一种解决方案。从语法上讲,Promise是一个对象,从它可以获取异步操作的消息。

Promise 状态

Promise 异步操作有三种状态:pending(进行中), fulfilled(已成功), rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。

const p1 = new Promise(function(resolve,reject){
    resolve('success1');
    resolve('success2');
}); 
const p2 = new Promise(function(resolve,reject){  
    resolve('success3'); 
    reject('reject');
});
p1.then(function(value){  
    console.log(value); // success1
});
p2.then(function(value){ 
    console.log(value); // success3
});

状态的缺点

a. 无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。

b. 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。

c. 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

then 方法

then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。

const p = new Promise(function(resolve,reject){
  resolve('success');
});
p.then(function(value){
  console.log(value);
});
console.log('first');
// first
// success

简便的Promise链式编程最好保持扁平化,不要嵌套Promise。

注意总是返回或终止Promise链。

4.2 Generator 函数

ES6新引入的 Generator函数,可以通过yield关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。

  • Generator 函数组成

    Generator 有两个区分于普通函数的部分:

    a. 一是在function 后面,函数名之前有个 *

    b. 函数内部有 yield 表达式。

    function* func(){
      console.log("one");
         yield '1';
         console.log("two");
         yield '2'; 
         console.log("three");
         return '3';
    }
    
  • 执行机制

    调用 Generator 函数和调用普通函数一样,在函数名后面加上()即可,但是 Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。

    let f = func()
    f.next()
    // one
    // {value: "1", done: false}
     
    f.next()
    // two
    // {value: "2", done: false}
     
    f.next()
    // three
    // {value: "3", done: true}
     
    f.next()
    // {value: undefined, done: true}
    

4.3 Async 函数

async 是 ES7 才有的与异步操作有关的关键字,和Promise, Generator 有很大关联的。

async 函数返回一个 Promise对象,可以使用 then 方法添加回调函数。

async function helloAsync(){
    return 'hello async'
}
console.log(helloAsync())   // Promise {<resolved>: 'helloAsync'}
helloAsync().then(v=>{
    console.log(v)            // hello async
})

async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会暂停执行,等到触发的异步操作完成后,恢复async 函数的执行并返回解析值。

await 关键字仅在 async function 中有效。

function testAwait(){
   return new Promise((resolve) => {
       setTimeout(function(){
          console.log("testAwait");
          resolve();
       }, 1000);
   });
}
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
 }
helloAsync(); 
// testAwait
// helloAsync

await 针对所跟不同表达式的处理方式:

  • Promise 对象: await 会暂停执行,等待 Promise 对象 resolve, 然后恢复async函数的执行并返回解析值。
  • 非Promise对象: 直接返回对应的值。

总结

N/A

参考

ES 6 教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容