ES2018 中 4 個有用的功能

ES2018 規範引入了四個新功能。這些功能包括異步迭代,rest/spread 屬性,Promise.prototype.finally() 和正則表達式改進。本問將幫你瞭解這些 ES2018 功能的工做原理及使用方法。javascript

異步迭代

異步迭代是討論的比較少 ES2018 功能之一。雖然還有不少關於 ES2018 其餘功能的討論,但幾乎沒有關於異步迭代這方面的內容。經過異步迭代,咱們能夠獲得異步的可迭代對象和迭代器。前端

這意味着你能夠把 await 關鍵字與 for…of 循環放在一塊兒使用。你能夠用這些循環對可迭代對象進行迭代。可迭代對象的包括數組、map、set,NodeList,函數的 arguments 參數,TypedArray 等。java

在 ES2018 以前,for...of 循環是同步的。若是你試着迭代涉及異步操做的可迭代對象並 await,則沒法正常工做。循環自己會保持同步,基本上忽略 await ,並在其內部的異步操做能夠完成以前完成迭代。git

// 下面的代碼在 ES2018 以前不起做用,由於循環保持同步。
// 建立一個異步函數:
async function processResponses(someIterable) {
  // 對可迭代對象進行迭代
  for (let item of someIterable) {
    // 經過異步操做處理項目,例如promise:
    await processItem(item)
  }
}

同時 for...of 循環也能夠與異步代碼一塊兒使用。也就是說能夠在遍歷可迭代對象時執行一些異步操做。for...of 循環將會是異步的,讓你可以等待異步操做完成。程序員

須要記住的是在哪裏使用 await 關鍵字。不須要把它放進循環體中,應該將其放在for...of關鍵字中 for 的後面。如今當你用 next() 方法獲取異步迭代器的下個值時,將會獲得一個 Promise。若是你想了解更多信息,能夠在 GitHub 上去看看(https://github.com/tc39/propo...)。github

// 建立一個異步函數:
async function processResponses(someIterable) {
  //遍歷可迭代對象並等待異步操做的結果
  for await (let item of someIterable) {
    processItem(item)
  }
}

Rest/Spread 屬性

restspread 並非真正的新功能。二者都是在 ES6 中做爲新的運算符引入的,它們很快就開始流行起來。能夠說 JavaScript 程序員喜歡它們。惟一的問題是它們只能用在數組和參數上,不過 ES2018 把這兩個功能引入了對象中。面試

restspread 運算符的語法都很是簡單,由三個點(...)組成。這些點後面是要在其上使用 restspread 運算符的對象。接下來簡單的討論一下二者的工做原理。正則表達式

對象的 rest 運算符

rest 運算符使你能夠將對象的全部剩餘對象屬性屬性提取到新對象上。要注意這些屬性必須是可枚舉的。若是你已經對某些屬性使用了分解,那麼 rest 運算符會只提取剩餘的屬性。數據庫

// Rest example:

const daysObj = {
  one: 'Monday',
  two: 'Tuesday',
  three: 'Wednesday',
  four: 'Thursday',
  five: 'Friday'
}

//使用解構將變量的前兩個屬性分配給變量。
//而後,使用rest將其他屬性分配給第三個變量。
const { one, two, ...restOfDays } = daysObj
// rest 僅提取 "three", "four" 和 "five" 
// 由於咱們已經提取了 "one" 和 "two" 

console.log(one)
// Output:
// 'Monday'

console.log(two)
// Output:
// 'Tuesday'

console.log(restOfDays)
// Output:
// { three: 'Wednesday', four: 'Thursday', five: 'Friday' }

若是要對對象使用 rest 運算符,須要記住兩點:首先,只能用一次,除非把它用在嵌套對象上。其次,必須在最後使用。這就是爲何在上面的例子中,在解構前兩個屬性以後而不是以前看到它的緣由。json

// 這行代碼不起做用,由於把 rest 運算符用在了最前面:
const { ...all, one, two } = { one: 1, two: 2, three: 3 }

//這行能起做用:
const { one, two, ...all } = { one: 1, two: 2, three: 3 }

// 這行不起做用,由於同一級別上有多個 rest 運算符:
const { one, ...some, ...end } = { /* some properties */ }

// 這行能起做用,在多個級別上的多個 rest 運算符:
const { one, {...secondLevel }, ...firstLevel } = { /* some properties */ }

對象的 spread 運算符

spread 運算符的做用是能夠經過插入另外一個對象的全部屬性來建立新對象。 Spread 運算符還容許你從多個對象插入屬性。也能夠把這個運算符與添加新屬性結合使用。

// Spread example:
const myOriginalObj = { name: 'Joe Doe', age: 33 }
// 用 spread 運算符建立新對象:
const myNewObj = { ...myOriginalObj }

console.log(myNewObj)
// Output:
// { name: 'Joe Doe', age: 33 }


// 添加屬性的例子:
const myOriginalObj = { name: 'Caesar' }
// 用 spread 運算符建立新對象
// 並添加新的屬性「genre」:
const myNewObj = { ...myOriginalObj, genre: 'Strategy' }

console.log(myNewObj)
// Output:
// {
//   name: 'Caesar',
//   genre: 'Strategy'
// }


// Spread 運算符併合並兩個對象:
const myObjOne = { title: 'Eloquent JavaScript' }
const myObjTwo = { author: 'Marijn Haverbeke' }

const myNewObj = { ...myObjOne, ...myObjTwo }

console.log(myNewObj)
// Output:
// {
//   title: 'Eloquent JavaScript',
//   author: 'Marijn Haverbeke'
// }

當從多個對象插入屬性並添加新屬性時,順序很重要

我來解釋一下,假設你要用 spread 運算符基於兩個現有對象建立一個新對象。第一個已有對象中包含具備某些值的屬性 title。第二個對象也包含屬性 title,可是值不同。最終到底取哪一個 title

答案是最後一個。若是對第一個對象使用 spread 運算符,而後再對第二個對象使用,則第二個 title 會生效。若是你將 spread 運算符永在第二個對象上,則第一個 title 會生效。

// Spread 運算符併合並兩個對象:
const myObjOne = {
  title: 'Eloquent JavaScript',
  author: 'Marijn Haverbeke',
}

const myObjTwo = {
  title: 'You Don\'t Know JS Yet',
  language: 'English'
}

// 用 spread 運算符經過組合 「myObjOne」 和 「myObjTwo」 建立新對象
// 注意:「myObjTwo」 中的 「title」 會將覆蓋 「myObjTwo」 的 「title」
// 由於「 myObjTwo」排在最後。
const myNewObj = { ...myObjOne, ...myObjTwo }

console.log(myNewObj)
// Output:
// {
//   title: "You Don't Know JS Yet",
//   author: 'Marijn Haverbeke',
//   language: 'English'
// }


// 注意:「myObjOne」 中的 「title」 將覆蓋 「myObjTwo」 的 「title」
const myNewObj = { ...myObjTwo, ...myObjOne }

console.log(myNewObj)
// Output:
// {
//   title: 'Eloquent JavaScript',
//   language: 'English',
//   author: 'Marijn Haverbeke'
// }

Promise.prototype.finally()

一開始有兩個用於 Promise 的回調函數。其中一個是 then(),在實現諾 Promise 執行。第二個是catch(),在 promise 被拒絕或 then() 拋出異常時執行。 ES2018 增長了用於 Promise 的第三個回調函數 finally()

每次完成 promise 時,都會執行 finally() 回調,無論 promise 是否完成。這個回調的通常用於執行應始終發生的操做。例如關閉模態對話框、關閉數據庫鏈接或進行某些清理。

// finally() example:
fetch()
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log(error))
  //最後作點什麼:
  .finally(() => console.log('Operation done.'))

對正則表達式的改進

ES2018 還對正則表達式功能進行了的一些改進。這些改進包括 s(dotAll) 標誌,後行斷言,命名捕獲組和 unicode 屬性轉義。

s(dotAll)

首先是 s(dotAll) 。與點(.)不一樣,s(dotAll) 容許對換行符及表情符號進行匹配。

// s(dotAll) example:
/hello.world/.test('hello\nworld')
// Output:
// false

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

後行斷言

在ES2018以前,JavaScript僅支持先行斷言。先行斷言用於基於其後的文原本匹配模式。在 ES2018 中增長了對後行斷言的支持。經過它能夠基於模式以前的文本模式來進行匹配。後行斷言的語法爲 ?<=

// 後行斷言例子:
/(?<=green) apple/.test('One red apple is on the table.')
// Output:
// false

/(?<=green) apple/.test('One green apple is on the table.')
// Output:
// true

斷言後面也有一個反向的回溯。僅當子字符串以前沒有斷言時,此斷言才與模式匹配。對後行斷言取反操做的語法是 ?<!

/(?<!green) apple/.test('One red apple is on the table.')
// Output:
// true

/(?<!green) apple/.test('One green apple is on the table.')
// Output:
// false

命名捕獲組

另外一個被 ES2018 引入到正則表達式的好功能是命名捕獲組。命名捕獲組的語法爲 ?<some_name>

const date_pattern = /(?<day>\d{2})\/(?<month>\d{2})\/(?<year>\d{4})/
const result = date_pattern.exec('11/12/2021')

console.log(result)
// Output:
// [
//   '11/12/2021',
//   '11',
//   '12',
//   '2021',
//   index: 0,
//   input: '11/12/2021',
//   groups: [Object: null prototype] { day: '11', month: '12', year: '2021' }
// ]

console.log(result.groups.day)
// Output:
// '11'

console.log(result.groups.month)
// Output:
// '12'

console.log(result.groups.year)
// Output:
// '2021'

Unicode 屬性轉義

每一個 unicode 字符都有許多屬性。例如:空白字符,大小寫,字母,ASCII,表情符號等。如今你能夠在正則表達式中訪問這些屬性了。

要使用這個功能須要作兩件事。首先必須使用 /u 標誌。這個標誌告訴 JavaScript 你的字符串是一系列 Unicode 代碼點。第二是使用 \p{}。你要檢查的屬性位於大括號之間,反之則用 \P{}

// 用俄語建立一個字符串(西裏爾字母):
const myStrCyr = 'Доброе утро'

//建立英文字符串(拉丁字母):
const myStrLat = 'Good morning'

//測試「 myStrCyr」是否包含西里爾字符:
/\p{Script=Cyrillic}/u.test(myStrCyr) // true

//測試「 myStrLat」是否包含西里爾字符:
/\p{Script=Cyrillic}/u.test(myStrLat) // false

// 測試「myStrLat」 是否包含西里爾字符:
/\p{Script=Latin}/u.test(myStrCyr) // false

// 測試「myStrLat」 是否包含拉丁語字符:
/\p{Script=Latin}/u.test(myStrLat) // true

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章


歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索