ES9已經來了 Are you ready?

ES9

前言

該篇文章主要是介紹了ES9新加的一些特性。javascript

1. 異步迭代

async/await的某些時刻,你可能嘗試在同步循環中調用異步函數。例如:java

async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}
複製代碼
複製代碼

這段代碼不會正常運行,下面這段一樣也不會:正則表達式

async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}
複製代碼
複製代碼

這段代碼中,循環自己依舊保持同步,並在在內部異步函數以前所有調用完成。數據庫

ES2018引入異步迭代器(asynchronous iterators),這就像常規迭代器,除了next()方法返回一個Promise。所以await能夠和for...of循環一塊兒使用,以串行的方式運行異步操做。例如:數組

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}
複製代碼

2. Promise.finally()

在ES6中,一個Promise鏈要麼成功進入最後一個then()要麼失敗觸發catch()。而實際中,咱們可能須要不管Promise不管成功仍是失敗,都運行相同的代碼。例如清除,刪除回話,關閉數據庫鏈接等操做。bash

ES9中,容許使用finally()來指定最終的邏輯。markdown

以下:異步

let count = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(100)
                }, 1000);
            })
        }
        let list = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve([1, 2, 3])
                }, 1000);
            })
        }

        let getList = async () => {
            let c = await count()
            console.log('async')
            let l = await list()
            return { count: c, list: l }
        }
        console.time('start');
        getList().then(res => {
            console.log(res)
        })
        .catch(err => {
            console.timeEnd('start')
            console.log(err)
        })
        .finally(() => {
            console.log('finally')
        }) 
        
        //執行結果
        async
        {count: 100, list: [1, 2, 3]}
        finally
複製代碼

3. Rest/Spread 屬性

3.1 ES6中(...)

在ES6中引入了三點...,做用主要是Rest參數和擴展運算符:async

做用對象僅用於數組函數

1.將一個未知數量的參數表示一個數組:

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}
複製代碼

2.擴展運算符:

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100
複製代碼

3.2 ES9中(...)

在ES9中爲對象提供了像數組同樣的Rest參數和展開運算符。

Rest參數用法

var obj = {
            a: 1,
            b: 2,
            c: 3
        }
        const { a, ...param } = obj;
        console.log(a)     //1
        console.log(param) //{b: 2, c: 3}
        
複製代碼

Spread用法,用於收集全部的剩餘參數:

var obj = {
            a: 1,
            b: 2,
            c: 3
        }
		function foo({a, ...param}) {
            console.log(a);
            console.log(param)
        }
複製代碼

跟數組同樣,Rest參數只能在聲明的結尾處使用。此外,它只適用於每一個對象的頂層,若是對象中嵌套對象則沒法適用。

擴展運算符能夠在其餘對象內使用

const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { ...obj1, z: 26 };
// obj2 is { a: 1, b: 2, c: 3, z: 26 }
複製代碼

3.3 Spread的使用場景

1.淺拷貝

能夠利用(...)來進行一個對象的拷貝,可是這種拷貝只能拷貝對象的可枚舉自有屬性。

var obj = {
            name: 'LinDaiDai',
            looks: 'handsome',
            foo() {
                console.log('old');
            },
            set setLooks(newVal) {
                this.looks = newVal
            },
            get getName() {
                console.log(this.name)
            }
        }

        var cloneObj = {...obj};
        cloneObj.foo = function() {
            console.log('new')
        };
        console.log(obj)     
        // { name: 'LinDaiDai',looks: 'handsome', foo: f foo(), get getName:f getName(), set setLooks: f setLooks(newVal)}
        console.log(cloneObj)
        // { name: 'LinDaiDai',looks: 'handsome', foo: f foo(), getName: undefined, setLooks: undefined }
        obj.foo()
        // old
        cloneObj.foo()
        // new 
複製代碼

如上所示,定義了一個對象obj 並使用(...)進行對象的拷貝,修改對象內的函數foo(),並不會影響原有的對象,可是原有對象的settergetter卻不能拷貝過去。

2.合併倆個對象

const merged = {...obj1, ...obj2};
//同:
const merged = Object.assign({}, obj1, obj2);
複製代碼

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

4.1 基本用法

Javascript正則表達式中使用exec()匹配可以返回一個對象,一個包含匹配字符串的類數組。

以下面案例中的匹配日期格式:

//正則表達式命名捕獲組
        const reDate = /(\d{4})-(\d{2})-(\d{2})/,
              match = reDate.exec('2018-08-06');
        console.log(match);
        // [2018-08-06, 2018, 08, 06]
        
        // 這樣就能夠直接用索引來獲取年月日:
        match[1] // 2018
        match[2] // 08
        match[3] // 06
複製代碼

返回一個數組,數組第0項爲與正則表達式相匹配的文本,第 1 個元素是與 RegExpObject 的第 1 個子表達式相匹配的文本(若是有的話),第 2 個元素是與 RegExpObject 的第 2 個子表達式相匹配的文本(若是有的話),以此類推。

上面的案例,如果改變正則表達式的結構就有可能改變匹配對象的索引。

如進行以下修改:

//正則表達式命名捕獲組
        const reDate = /(\d{2})-(\d{2})-(\d{4})/,
              match = reDate.exec('2018-08-06');
        console.log(match);
        // [2018-08-06, 08, 06, 2018]
        
        // 但此時年月日的索引就改變了
        match[3] // 2018
        match[1] // 08
        match[2] // 06
複製代碼

能夠看到上面寫法的弊端,所以在ES9中容許命名捕獲組使用符號?<name>,以下:

const reDate = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
              match = reDate.exec('2018-08-06')
        console.log(match);
        // [2018-08-06, 08, 06, 2018, groups: {day: 06, month: 08, year: 2018}]
        
        //此時可使用groups對象來獲取年月日
        match.groups.year // 2018
        match.groups.month // 08
        match.groups.day  // 06
複製代碼

命名捕獲組的寫法至關因而把每一個匹配到的捕獲組都定義了一個名字,而後存儲到返回值的groups屬性中。

4.2 結合replace()

命名捕獲也可使用在replace()方法中。例如將日期轉換爲美國的 MM-DD-YYYY 格式:

const reDate = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
      d = '2018-08-06'
      USADate = d.replace(reDate, '$<month>-$<day>-$<year>');
console.log(USADate);
// 08-06-2018
複製代碼

還能夠將中文名的姓和名調換:

const reName = /(?<sur>[a-zA-Z]+)-(?<name>[a-zA-Z]+)/;
      Chinese = 'Lin-DaiDai',
      USA = Chinese.replace(reName, '$<name>-$<sur>');
console.log(USA);
// DaiDai-Lin
複製代碼

5. 正則表達式反向斷言

5.1 基本用法

先來看下正則表達式先行斷言是什麼:

如獲取貨幣的符號

const noReLookahead = /\D(\d+)/,
        	  reLookahead = /\D(?=\d+)/,
        	  match1 = noReLookahead.exec('$123.45'),
              match2 = reLookahead.exec('$123.45');
        console.log(match1[0]); // $123   
        console.log(match2[0]); // $
複製代碼

能夠看到如果在正則表達式中加入?=的話,匹配會發生,但不會有任何捕獲,而且斷言沒有包含在整個匹配字段中。

在ES9中能夠容許反向斷言:

const reLookahead = /(?<=\D)[\d\.]+/;
              match = reLookahead.exec('$123.45');
        console.log(match[0]); // 123.45
複製代碼

使用?<=進行反向斷言,可使用反向斷言獲取貨幣的價格,而忽略貨幣符號。

5.2 確定反向斷言

上面的案例爲確定反向斷言,也就是說\D這個條件必須存在,如果:

const reLookahead = /(?<=\D)[\d\.]+/;
              match1 = reLookahead.exec('123.45'),
              match2 = reLookahead.exec('12345');
        console.log(match1[0]); // 45
        console.log(match2);  // null
複製代碼

能夠看到match1匹配到的是45,這是因爲在123前面沒有任何符合\D的匹配內容,它會一直找到符合\D的內容,也就是.而後返回後面的內容。

而如果沒有知足前面確定反向斷言的條件的話,則返回null.

6. 正則表達式dotAll模式

正則表達式中點.匹配除回車外的任何單字符,標記s改變這種行爲,容許行終止符的出現:

/hello.world/.test('hello\nworld');  // false

/hello.world/s.test('hello\nworld'); // true

console.log(/hello.world/s.test(`hello
world`))   // true
複製代碼

7. 正則表達式 Unicode 轉義

到目前爲止,在正則表達式中本地訪問 Unicode 字符屬性是不被容許的。ES2018添加了 Unicode 屬性轉義——形式爲\p{...}\P{...},在正則表達式中使用標記 u (unicode) 設置,在\p塊兒內,能夠以鍵值對的方式設置須要匹配的屬性而非具體內容。

const reGreekSymbol = /\p{Script=Greek}/u;
    console.log(reGreekSymbol.test('π')); // true
複製代碼

Greek爲希臘語的意思。

8. 非轉義序列的模板字符串

最後,ES2018 移除對 ECMAScript 在帶標籤的模版字符串中轉義序列的語法限制。

以前,\u開始一個 unicode 轉義,\x開始一個十六進制轉義,\後跟一個數字開始一個八進制轉義。這使得建立特定的字符串變得不可能,例如Windows文件路徑 C:\uuu\xxx\111。更多細節參考模板字符串

後語

知識產權無價,支持原創。

參考文章:

[譯] ES2018(ES9)的新特性

【譯】ES2018 新特性:Rest/Spread 特性

ES6/ES7/ES8/ES9 能夠看到javascript並無讓咱們失望,這幾年的更新沒有落下,不間斷的學習,才能保證本身被這個社會淘汰...

相關文章
相關標籤/搜索