ECMAScript 2016,2017 和 2018 中全部新功能的示例

很難追蹤 JavaScript(ECMAScript)中的新功能。 想找到有用的代碼示例更加困難。html

所以,在本文中,我將介紹 TC39 已完成 ES2016,ES2017 和 ES2018(最終草案)提案中全部添加的 18 個功能,並展現有用的示例。前端

這是一個很長的文章,但應該很容易閱讀。 能夠把它想象成 「Netflix binge reading」。可是到文章結束,我保證你將對全部這些功能有很好的瞭解。git

好的,讓咱們逐個討論這些問題。github

ECMAScript 2016

1.Array.prototype.includes

includes 是 Array 上的一個簡單實例方法,有助於輕鬆查找某項元素是否在數組中(包括NaN ,與 indexOf不一樣)。web

ECMAScript 2016 或者 ES7?—?Array.prototype.includes()

人們想要 contains 來命名該規範,但顯然 Mootools 已經使用過這個命名,因此使用了 includes正則表達式

更多示例和常見問題請參見:ES2016 新特性:npm scripts : 每一個前端開發都應知道的一些使用提示數據庫

2.指數運算符

加法和減法等數學運算分別具備 + 和 - 等中綴運算符。與它們相似,** 中綴運算符一般用於指數運算。在 ECMAScript 2016 中,引入了 ** 代替 Math.pow 。express

ECMAScript 2016 or ES7?—?** 中綴運算符

更多示例和常見問題請參見:ES2016 新特性:求冪運算符(**)npm

ECMAScript 2017

1.Object.values()

Object.values() 是一個與 Object.keys() 相似的新函數,但返回 Object 自身屬性的全部值,不包括原型鏈中的任何值。數組

ECMAScript 2017 (ES8)— Object.values()

更多示例和常見問題請參見:ES2017 新特性:Object.entries() 和 Object.values()

2.Object.entries()

Object.entries() 與 Object.keys 相關,但它不只僅返回 keys ,而是以數組方式返回 keys 和 values 。這使得在循環中使用對象或將對象轉換爲 Maps 等操做變得很是簡單。

示例1

ECMAScript 2017 (ES8)?—?在循環中使用 Object.entries()

示例2

ECMAScript 2017 (ES8)?—?使用 Object.entries() 將 Object 轉換爲 Map

更多示例和常見問題請參見:ES2017 新特性:Object.entries() 和 Object.values()

3.字符串填充

String 中添加了兩個實例方法,String.prototype.padStart 和 String.prototype.padEnd – 容許將空字符串或其餘字符串附加到原始字符串的開頭或結尾。

JavaScript 代碼:
    'someString'.padStart(numberOfCharcters [,stringForPadding]);
     
    '5'.padStart(10) // ' 5'
    '5'.padStart(10, '=*') //'=*=*=*=*=5'
     
    '5'.padEnd(10) // '5 '
    '5'.padEnd(10, '=*') //'5=*=*=*=*='

當咱們想要對齊字符串的長度的時候,能夠很是方便的使用這兩個函數。

3.1 padStart 示例

在下面的示例中,咱們列出了不一樣長度的數字。咱們但願前置「0」,以便全部項具備相同的 10 位數長度顯示。咱們可使用 padStart(10, '0') 輕鬆實現這一目標。

ECMAScript 2017?—?padStart 示例

3.2 padEnd 示例

當咱們打印不一樣長度的多個項並但願正確對齊它們時,padEnd 真的很方便。

下面的示例是 padEndpadStart 和 Object.entries 組合在一塊兒以產生漂亮輸出的一個很好的現實示例。

ECMAScript 2017?—?padEnd, padStart 和 Object.Entries 示例

JavaScript 代碼:
    const cars = {
    '🚙BMW': '10',
    '🚘Tesla': '5',
    '🚖Lamborghini': '0'
    }
    Object.entries(cars).map(([name, count]) => {
    //padEnd appends ' -' until the name becomes 20 characters
    //padStart prepends '0' until the count becomes 3 characters.
    console.log(`${name.padEnd(20, ' -')} Count: ${count.padStart(3, '0')}`)
    });
    //Prints..
    // 🚙BMW - - - - - - - Count: 010
    // 🚘Tesla - - - - - - Count: 005
    // 🚖Lamborghini - - - Count: 000

3.3 ⚠️padStart 和 padEnd 用於 Emojis 表情和其餘雙字節字符

Emojis 和其餘雙字節字符使用多個字節的 unicode 表示。 因此 padStart 和 padEnd 可能沒法按預期工做!⚠️

例如:假設咱們把字符串 heart 經過 emoji表情 ❤️ 使用 padStart 延長到十個字節,這個時候咱們獲得以下的輸出:

JavaScript 代碼:
  1. //Notice that instead of 5 hearts, there are only 2 hearts and 1 heart that looks odd!
  2. 'heart'.padStart(10, "❤️"); // prints.. '❤️❤️❤heart'

這是由於 ❤️ 自己佔據兩個字節(\u2764\uFE0F),而 heart 自己有 5 個字節,因此咱們只剩 5 個字節的位置能夠填充,JS使用\u2764\uFE0F 來填充兩顆心併產生 ❤️❤️ 。對於最後一個,它只使用 heart \u2764的第一個字節產生 ❤

因此咱們最終獲得:❤️❤️❤heart

PS:你可使用 此連接 查看 unicode 字符轉換。

更多示例和常見問題請參見:ES2017 新特性:字符串方法:padStart 和 padEnd

4.Object.getOwnPropertyDescriptors

此方法返回給定對象的全部屬性的全部詳細信息(包括 getter get 和 setter set方法)。 添加它的主要動機是容許淺複製/克隆對象到另外一個對象,該對象也複製 getter 和 setter 函數而不像 Object.assign

Object.assign 淺複製除原始源對象的 getter 和 setter 函數以外的全部信息。

下面的示例顯示了 Object.assign 和 Object.getOwnPropertyDescriptors 以及 Object.defineProperties之間的區別,以將原始對象 Car 複製到新對象 ElectricCar 中。 你將看到,經過使用 Object.getOwnPropertyDescriptorsdiscount getter 和 setter 函數也會複製到目標對象中。

以前…

以前?—?使用 Object.assign

之後…

ECMAScript 2017 (ES8)?—?Object.getOwnPropertyDescriptors

JavaScript 代碼:
    var Car = {
    name: 'BMW',
    price: 1000000,
    set discount(x) {
    this.d = x;
    },
    get discount() {
    return this.d;
    },
    };
    // 打印 Car 對象 'discount' 屬性的詳細信息
    console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));
    // 打印 ..
    // {
    // get: [Function: get],
    // set: [Function: set],
    // enumerable: true,
    // configurable: true
    // }
     
    //使用 Object.assign 將 Car 的屬性複製到 ElectricCar
    const ElectricCar = Object.assign({}, Car);
     
    // 打印 ElectricCar 對象 'discount' 屬性的詳細信息
    console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'));
    // 打印 ..
    // {
    // value: undefined,
    // writable: true,
    // enumerable: true,
    // configurable: true
    // }
    //⚠️ 請注意,ElectricCar 對象中的 'discount' 屬性缺乏 getter 和 setter !👎👎
     
    // 使用 Object.defineProperties 將 Car 的屬性複製到 ElectricCar2 ,
    // 並使用 Object.getOwnPropertyDescriptors 提取 Car的屬性
    const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car));
     
    // 打印 ElectricCar2 對象的 'discount' 屬性的詳細信息
    console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount'));
    // 打印..
    // { get: [Function: get], 👈🏼👈🏼👈🏼
    // set: [Function: set], 👈🏼👈🏼👈🏼
    // enumerable: true,
    // configurable: true
    // }
    // 請注意,ElectricCar2 對象中的 'discount' 屬性存在 getter 和 setter !

更多示例和常見問題請參見:ES2017 新特性:Object.getOwnPropertyDescriptors()

5.在函數參數中添加尾逗號

這是一個小更新,容許咱們在函數最後一個參數後面有逗號。 爲何? 幫助使用像 git blame 這樣的工具,防止添加一個參數卻須要修改兩行代碼。

如下示例顯示了問題和解決方案。

ECMAScript 2017 (ES 8)?—?函數參數中的尾逗號

注意:你也能夠在調用函數時使用尾逗號!

更多示例和常見問題請參見:ES2017 新特性:函數參數列表和調用尾逗號

6. Async/Await

到目前爲止,這個特性應該是目前爲止是最重要和最有用的功能。async 函數解決了回調地獄的問題,並使整個代碼看起來簡單。

async 關鍵字告訴 JavaScript 編譯器以不一樣方式處理函數。 只要到達該函數中的 await 關鍵字,編譯器就會暫停。 它假定 await 以後的表達式返回一個 promise 並等待,直到 promise 被 resolved 或被 rejected ,而後才繼續執行。

在下面的示例中,getAmount 函數調用兩個異步函數 getUser 和 getBankBalance。 咱們能夠用 Promise 作到這一點,可是使用 async await 更加優雅和簡單。

ECMAScript 2017 (ES 8)?—?Async Await 基礎示例

6.1 Async 函數自己返回一個 Promise 。

若是你正在等待 async 函數的結果,則須要使用 Promise 的 then 語法來捕獲其結果。

在如下示例中,咱們但願使用 console.log 但不在 doubleAndAdd 中記錄結果。 因此咱們想等待並使用 then 語法將結果傳遞給console.log 。

ECMAScript 2017 (ES 8)?—?Async Await 自己返回 Promise

6.2 並行調用 async/await

在前面的例子中,咱們調用 await 兩次,但每次咱們等待一秒鐘(總共2秒)。相反,咱們能夠並行調用它,由於使用 Promise.all 並行調用 a 和 b 。

ECMAScript 2017 (ES 8)?—?使用 Promise.all 並行調用 async/await

6.3 async/await 函數的錯誤處理

使用 async/await 時,有多種方法能夠處理錯誤。

選項1-在函數中使用try catch

ECMAScript 2017?—?在 async/await 函數中使用try catch

JavaScript 代碼:
    //Option 1 - Use try catch within the function
    async function doubleAndAdd(a, b) {
    try {
    a = await doubleAfter1Sec(a);
    b = await doubleAfter1Sec(b);
    } catch (e) {
    return NaN; //return something
    }
    return a + b;
    }
    //🚀Usage:
    doubleAndAdd('one', 2).then(console.log); // NaN
    doubleAndAdd(1, 2).then(console.log); // 6
    function doubleAfter1Sec(param) {
    return new Promise((resolve, reject) => {
    setTimeout(function() {
    let val = param * 2;
    isNaN(val) ? reject(NaN) : resolve(val);
    }, 1000);
    });
    }

選項2-捕獲(Catch) await 表達式

因爲每個 await 表達式返回的都是 Promise,咱們能夠直接在每一行上面添加 catch。

ECMAScript 2017?—?await 表達式使用 try catch

JavaScript 代碼:
    //Option 2 - *Catch* errors on every await line
    //as each await expression is a Promise in itself
    async function doubleAndAdd(a, b) {
    a = await doubleAfter1Sec(a).catch(e => console.log('"a" is NaN')); // 👈
    b = await doubleAfter1Sec(b).catch(e => console.log('"b" is NaN')); // 👈
    if (!a || !b) {
    return NaN;
    }
    return a + b;
    }
     
    //🚀Usage:
    doubleAndAdd('one', 2).then(console.log); // NaN and logs: "a" is NaN
    doubleAndAdd(1, 2).then(console.log); // 6
     
    function doubleAfter1Sec(param) {
    return new Promise((resolve, reject) => {
    setTimeout(function() {
    let val = param * 2;
    isNaN(val) ? reject(NaN) : resolve(val);
    }, 1000);
    });
    }

選項3-捕獲(Catch) 整個async-await函數

ECMAScript 2017?—?在結尾捕獲(Catch) 整個async/await函數

JavaScript 代碼:
    //Option 3 - Dont do anything but handle outside the function
    //since async / await returns a promise, we can catch the whole function's error
    async function doubleAndAdd(a, b) {
    a = await doubleAfter1Sec(a);
    b = await doubleAfter1Sec(b);
    return a + b;
    }
     
    //🚀Usage:
    doubleAndAdd('one', 2)
    .then(console.log)
    .catch(console.log); // 👈👈🏼<------- use "catch"
     
    function doubleAfter1Sec(param) {
    return new Promise((resolve, reject) => {
    setTimeout(function() {
    let val = param * 2;
    isNaN(val) ? reject(NaN) : resolve(val);
    }, 1000);
    });
    }

ECMAScript 2018

1. 共享內存和 atomics

這是一個巨大的,很是先進的功能,而且是對 JS 引擎的核心加強。

這個特性的主要目的是給 JavaScript 提供多線程功能,以便JS開發人員經過本身管理內存來編寫高性能的併發程序,而不是讓JS引擎管理內存。

這是經過一種名爲 SharedArrayBuffer 的新型全局對象完成的,該對象實質上將數據存儲在共享內存空間中。所以,這些數據能夠在主JS線程和 Web-worker 線程之間共享。

以前,若是咱們想在主 JS 線程和 web-worker 之間共享數據,咱們必須複製數據並使用 postMessage 將其發送到另外一個線程。

如今,你只需使用 SharedArrayBuffer ,主線程和多個 web-worker 線程均可以當即訪問數據。

可是在線程之間共享內存會致使競爭條件(即多個進程同時操做一個內存)。爲了幫助避免競爭條件,引入了 Atomics 全局對象。 Atomics 提供了各類方法來在線程使用其數據時鎖定共享內存。它還提供了安全地更新共享內存中的此類數據的方法。

建議經過某個庫使用此功能,可是如今沒有基於此功能構建的庫。

若是你有興趣,我建議閱讀:

  1. From Workers to Shared Memory?—?lucasfcosta
  2. A cartoon intro to SharedArrayBuffers?—?Lin Clark
  3. Shared memory and atomics?—?Dr. Axel Rauschmayer

2.移除了標記模板字面量的限制

首先,咱們須要澄清「標記模板字面量」是什麼,以便咱們更好地理解這個功能。

在 ES2015+ 中,有一個稱爲標記模板文字的功能,容許開發人員自定義字符串的插值方式。 例如,在標準方式中,字符串被插入以下…

標記模板字面量

在標記的字面量中,你能夠編寫一個函數來接收字符串字面量的硬編碼部分,例如 ['Hello','!'] 而且替換變量,例如 ['Raja'] ,做爲參數進入一個自定義函數(例如 greet ),並從該自定義函數返回任何你想要的內容。

下面的示例顯示咱們的自定義 「Tag」 函數 greet,如「Good Morning」 「Good afternoon」,等等,取決於當天到字符串字面量的時間,並返回自定義字符串。

JavaScript 代碼:
    //A "Tag" function returns a custom string literal.
    //In this example, greet calls timeGreet() to append Good //Morning/Afternoon/Evening depending on the time of the day.
    function greet(hardCodedPartsArray, ...replacementPartsArray) {
    console.log(hardCodedPartsArray); //[ 'Hello ', '!' ]
    console.log(replacementPartsArray); //[ 'Raja' ]
     
    let str = '';
    hardCodedPartsArray.forEach((string, i) => {
    if (i < replacementPartsArray.length) {
    str += `${string} ${replacementPartsArray[i] || ''}`;
    } else {
    str += `${string} ${timeGreet()}`; //<-- append Good morning/afternoon/evening here
    }
    });
    return str;
    }
     
    //��Usage:
    const firstName = 'Raja';
    const greetings = greet`Hello ${firstName}!`; //����<-- Tagged literal
     
    console.log(greetings); //'Hello Raja! Good Morning!' ��
     
    function timeGreet() {
    const hr = new Date().getHours();
    return hr < 12
    ? 'Good Morning!'
    : hr < 18 ? 'Good Afternoon!' : 'Good Evening!';
    }

如今咱們討論了 「Tagged」 函數是什麼,許多人想要在不一樣的場景下中使用此功能,例如在終端中使用命令和 HTTP 請求來編寫 URI ,等等。

⚠️標籤字符串模版存在的問題

ES2015 和 ES2016 規範不容許使用轉義字符,如 \u(unicode),\x(十六進制),除非它們看起來徹底像 \u00A9 或 \u{2F804} 或 \xA9 。

所以,若是你有一個內部使用其餘域規則(如終端規則)的 Tagged 函數,可能須要使用 \ubla123abla ,而不能是 \u0049 或 \u{@F804} ,這樣你會獲得一個語法錯誤。

在 ES2018 中,只要 Tagged 函數返回具備 「cooked」 屬性(無效字符爲 「undefined」 )的對象中的值,而後是 「raw」 屬性( 不管你想要什麼)。

JavaScript 代碼:
    function myTagFunc(str) {
    return { "cooked": "undefined", "raw": str.raw[0] }
    }
     
    var str = myTagFunc `hi \ubla123abla`; //call myTagFunc
     
    str // { cooked: "undefined", raw: "hi \\unicode" }

3. 正則表達式中的 ‘dotall’ 標記

目前在RegEx中,點(「.」)能夠表示任何的單一字符,但它不能與 \n , \r\f 等換行符匹配。 例如:

JavaScript 代碼:
    //Before
    /first.second/.test('first\nsecond'); //false

此加強功能使點運算符能夠匹配任何單個字符。爲了確保不會破壞任何內容,咱們須要在建立RegEx時使用 \s 標記才能使其正常工做。

JavaScript 代碼:
    //ECMAScript 2018
    /first.second/s.test('first\nsecond'); //true Notice: /s 👈🏼

如下是 提案 文檔中的總體API:

ECMAScript 2018?—?Regex dotAll feature allows matching even \n via 「.」 via /s flag

4. 正則表達式捕獲命名組

這個加強功能帶來了其餘語言(如Python,Java等)的有用 RegExp 功能,稱爲「命名組」。這個功能容許容許正則表達式給每個捕獲組起一個名字 (?<name>...),而後,咱們可使用該名稱輕鬆獲取咱們須要的任何羣組。

4.1 基本的命名組例子

在下面的例子中,咱們使用 (?<year>) (?<month>) (?<day>) 來爲正則表達式中的不一樣部分分組,結果對象中會包含一個 groups 屬性,其擁有 year month day 三個對象。

ECMAScript 2018?—?Regex named groups example

4.2 在正則表達式自己內使用命名組

咱們可使用 \k<group name> 格式來反向引用正則表達式自己中的組。如下示例顯示了它的工做原理。

ECMAScript 2018?—?Regex named groups back referencing via

4.3 在 String.prototype.replace 中使用命名組

命名組也能夠在 String 的 replace 方法中使用,好比用來交換一個字符串中各個部分的位置。

例如,將firstName, lastName 更改成 lastName, firstName

ECMAScript 2018?—?Using RegEx’s named groups feature in replace function

5. 對象的剩餘屬性

Rest 運算符 ...(三個點)容許咱們提取 Object 的剩餘屬性。

5.1 咱們可使用展開運算符展開咱們想要的屬性:

ECMAScript 2018?—?Object destructuring via rest

5.2 或者咱們藉助展開運算符,移除咱們不想要的屬性 🔥🔥

ECMAScript 2018?—?Object destructuring via rest

6.展開對象的屬性

展開屬性看起來就像 Rest 運算符,都是三個點 ...,但不一樣之處在於你使用展開操做符來建立(重構)新對象。

提示:展開(spread)運算符用於等號的右側。剩餘(Rest)運算符用在等號的左側。

ECMAScript 2018?—?經過展開(spread)運算符進行對象重構

7.正則表達式後行斷言(Lookbehind)

這是 RegEx 的一個加強,它容許咱們確保某些子字符串剛好出如今某些子字符串以前。

你如今可使用一個組 (?<=…)(問號,小於,等於)來查看先行斷言。

此外,你可使用 (?<!…)(問號,小於,感嘆號)來查看後行斷言。基本上,只要-ve斷言經過,這將匹配。

確定斷言:假設咱們要確保 # 符號存在於 winning 以前(即:#winning),並但願正則表達式只返回字符串 「winning」 。下面是咱們的作法:

ECMAScript 2018?—?(?<=…) for positive assertion

否認斷言:假設咱們想要從具備  符號的行中提取數字,而不是 $ 。

ECMAScript 2018?—?(?<!…) for negative assertions

8. RegExp Unicode屬性轉義

提案連接:https://github.com/tc39/proposal-regexp-unicode-property-escapes

編寫 RegEx 以匹配各類 unicode 字符並不容易。像 \w\W\d 等只匹配英文字符和數字。可是其餘語言中的數字如印地語,希臘語等等該怎麼辦呢?

這就是 Unicode 屬性轉義的用武之地。事實證實,Unicode 爲每一個符號(字符)添加元數據屬性,並使用它來分組或表徵各類符號。

例如,Unicode 數據庫將全部印地語字符(??????)歸爲一個名爲 Script 的屬性,其值爲 Devanagari ,另外一個屬性爲Script_Extensions,其值爲 Devanagari 。因此咱們能夠搜索 Script=Devanagari 並得到全部印地語字符。

梵文能夠用於各類印度語言,如馬拉地語,印地語,梵語等。

從 ECMAScript 2018 開始,咱們可使用 \p 來轉義字符以及 {Script = Devanagari} 以匹配全部這些印度字符。也就是說,咱們能夠在 RegEx 中使用:\p{Script=Devanagari} 來匹配全部梵文字符。

ECMAScript 2018?—?showing p

JavaScript 代碼:
  1. //The following matches multiple hindi character
  2. /^\p{Script=Devanagari}+$/u.test('हिन्दी'); //true
  3. //PS:there are 3 hindi characters h

一樣,Unicode 數據庫將 Script_Extensions(和 Script )屬性下的全部希臘字符組合爲希臘語。 因此咱們可使用 Script_Extensions=Greek 或 Script=Greek 搜索全部希臘字符。

也就是說,咱們能夠在RegEx中使用: \p{Script=Greek} 來匹配全部希臘字符。

ECMAScript 2018?—?showing p

JavaScript 代碼:
  1. //The following matches a single Greek character
  2. /\p{Script_Extensions=Greek}/u.test('π'); // true

此外,Unicode數據庫在布爾屬性 Emoji ,Emoji_Component, Emoji_Presentation ,Emoji_Modifier和 Emoji_Modifier_Base 下存儲各類類型的 Emojis,其屬性值爲 true。 所以,咱們只需選擇 Emoji 符號便可搜索全部表情符號。

也就是說,咱們可使用:\p{Emoji}\Emoji_Modifier 等來匹配各類 Emojis 。

如下示例將使一切清楚。

ECMAScript 2018?—?showing how \p can be used for various emojis

JavaScript 代碼:
    //The following matches an Emoji character
    /\p{Emoji}/u.test('❤️'); //true
     
    //The following fails because yellow emojis don't need/have Emoji_Modifier!
    /\p{Emoji}\p{Emoji_Modifier}/u.test('✌️'); //false
     
    //The following matches an emoji character\p{Emoji} followed by \p{Emoji_Modifier}
    /\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true
     
    //Explaination:
    //By default the victory emoji is yellow color.
    //If we use a brown, black or other variations of the same emoji, they are considered
    //as variations of the original Emoji and are represented using two unicode characters.
    //One for the original emoji, followed by another unicode character for the color.
    //
    //So in the below example, although we only see a single brown victory emoji,
    //it actually uses two unicode characters, one for the emoji and another
    // for the brown color.
    //
    //In Unicode database, these colors have Emoji_Modifier property.
    //So we need to use both \p{Emoji} and \p{Emoji_Modifier} to properly and
    //completely match the brown emoji.
    /\p{Emoji}\p{Emoji_Modifier}/u.test('✌🏽'); //true

最後,咱們可使用大寫「P」( \P )轉義字符,而不是小寫「p」( \p )來否認匹配。

參考:

9.Promise.prototype.finally()

finally() 是一個添加到 Promise 實例的新方法。 主要考慮是容許在 resolve 或 reject 調用以後執行一些清理性質的代碼。finally 被執行的時候不會被傳入任何函數,而且不管何時都會被執行。

咱們來看看各類狀況。

ECMAScript 2018?—?finally() 在 resolve 狀況下

ECMAScript 2018?—?finally() in reject case

ECMASCript 2018?—?finally() in Error thrown from Promise case

ECMAScript 2018?—?Error thrown from within **catch** case

10.異步迭代

這是一個很是有用的特性。 基本上它容許咱們輕鬆建立異步代碼循環!

此特性添加了一個新的「for-await-of」循環,容許咱們在循環中調用返回 promises(或帶有一堆 promise 的 Arrays )的異步函數。 循環會等待每一個 Promise 在進行下一個循環以前 resolve 。

ECMAScript 2018?—?Async Iterator via for-await-of

相關文章
相關標籤/搜索