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閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容