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

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

原文連接: medium.freecodecamp.org
翻譯連接:https://www.zcfy.cc/article/h...html

始終緊跟Javascript(ECMAScript)的最新功能是很難的,更難的是找到有用的代碼示例。git

所以,在本文中,我將介紹在TC39的最終提案中包含的18個功能,這些功能將被添加到ES2016, ES2017, and ES2018 (最終草案)中,並經過有實際用處的示例來展現它們。github

這是一篇很是長的貼子,但應該很是容易閱讀。把它看成「Netflix狂歡閱讀」 讀完以後,我保證你將掌握大量有關這些功能的知識。web

讓咱們一個一個地看看這些新功能正則表達式

1. Array.prototype.includes

includes是Array實例上一個很是簡的方法,能夠很是容易查找到某一項是否在Array中(包括 NaN 而不像 indexOf不能查找NaN)。數據庫

const arr = [1, 2, 3, 4, NaN];
// Instead of
if (arr.indexOf(3) >= 0) {
    console.log(true);
}

// Use
if (arr.includes(3)) {
    console.log(true);
}

arr.includes(NaN); // true
arr.indexOf(NaN); // -1
花絮:JavaScript規範制定者想將它命名爲contains,可是這個名字已經明確地被Mootools用了,所他們使用includes。

2. 指數 運算符

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

Math.pow(7,2) // 49

//Now
7**2 // 49

一、Object.values()
Object.values()是一個相似於Object.keys()的新函數,它返回對象自有屬性的全部值,而不包括原型鏈上的任何值。數組

const cars = { BMW: 2, TESLA: 2, TOYOTA: 1 };

// ES2015
const vals = Object.keys(cars).map(key => cars[key]);
console.log(vals); // [3,2,1]

// ES2017
const values = Object.values(cars);
console.log(values);

二、 Object.entries()
Object.entries()與Object.keys有關,但不只僅返回鍵,而是以數組的形式返回鍵和值。這使得在循環中使用對象,或將對象轉到Maps等變得很是簡單。promise

示例 1:安全

const cars = { BMW: 2, TESLA: 2, TOYOTA: 1 };

// ES5.1
Object.keys(cars).forEach(function(key) {
    console.log('key: ' + key + 'value' + carsp[key]);
});

// ES8
for (let [key, value] of Object.entries(cars)) {
    console.log(`key: ${key} value:${value}`);
}

示例 2:

const cars = { BMW: 2, TESLA: 2, TOYOTA: 1 };

// ES2015
const map1 = new Map();
Object.keys(cars).forEach(key => {
    map1.set(key, cars[key]);
});

console.log(map1); // Map(3){"BMW" => 2, "TESLA" => 2, "TOYOTA" => 1}

// ES8
const map = new Map(Object.entries(cars));
console.log(map); // Map(3){"BMW" => 2, "TESLA" => 2, "TOYOTA" => 1}

3. 字符串填充

String新增了兩個實例謝謝老婆---String.prototype.padStart 和 String.prototype.padEnd ,它們容許在原始字符串的開始和結尾處添加空字符串或者是其它的字符串。

'someString'.padStart(字符串的總長度 [,填充字符串]);

'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
const formatted = [0, 1, 12, 123, 1234, 12345].map(num =>
    num.toString().padStart(10, '0')
);
console.log(formatted);// ["0000000000", "0000000001", "0000000012", "0000000123", "0000001234", "0000012345"]

3.2 padEnd 示例:
當咱們想右對齊打印多個不一樣長度的項目時,使用padEnd就很是方便了。

下面是一個很是好實例,它展現了padEnd 、padStart 和 Object.entries如何結合使用產生一個漂亮的輸出。

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 ⚠️ Emojis和其餘雙字節字符上的padStart和padEnd
Emojis和其它雙字節字符串使用unicode的多字節表示。因此padStart和padEnd可能沒法按照預期的工做⚠️。

例如:假設咱們嘗試用 ❤️表示符號填充字符中heart到10個字符,結果以下所示:

//Notice that instead of 5 hearts, there are only 2 hearts and 1 heart that looks odd!

'heart'.padStart(10, "❤️");
// prints.. '❤️❤️❤heart'

這是由於 ❤️是2個碼長('u2764uFE0F')!單詞heart是5個字符,因此我僅剩下5個字符用於填充。因此最終JS使用'u2764uFE0F'填充了兩個心型❤️❤️。最一個❤的產生是由於JS僅使用了第一個字節u2764。

因此最後輸出: ❤️❤️❤heart

PS:你可經過這個連接查看unicode字符轉換。

4. Object.getOwnPropertyDescriptors

這個方法返回一個對象的全部屬性的詳細信息(包括get和set方法)。添加這個方法的主要目的是容許淺拷貝/克隆一個對象,包括對象上的getter和setter函數,不一樣於Object.assign。

Object.assign 容許淺複製源對象上的全部屬性的詳細信息,除getter和setter函數外。

下面的示例經過拷貝一個原始對象Car到一個新對象ElectricCar,來展現Object.assign和Object.getOwnPropertyDescriptors以及Object.defineProperties之間的區別。你會發現使用Object.getOwnPropertyDescriptors,discount的getter和setter函數一樣被拷貝到目標對象。

var Car = {

 name: 'BMW',

 price: 1000000,

 set discount(x) {

  this.d = x;

 },

 get discount() {

  return this.d;

 },

};
//Print details of Car object's 'discount' property

console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));

//prints..

// { 

//   get: [Function: get],

//   set: [Function: set],

//   enumerable: true,

//   configurable: true

// }
//Copy Car's properties to ElectricCar using Object.assign

const ElectricCar = Object.assign({}, Car);
//Print details of ElectricCar object's 'discount' property

console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount'));

//prints..

// { 

//   value: undefined,

//   writable: true,

//   enumerable: true,

//   configurable: true 


// }

//⚠️Notice that getters and setters are missing in ElectricCar object for 'discount' property !??
//Copy Car's properties to ElectricCar2 using Object.defineProperties 

//and extract Car's properties using Object.getOwnPropertyDescriptors

const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car));
//Print details of ElectricCar2 object's 'discount'
property

console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount'));

//prints..

// { get: [Function: get],  ??????

//   set: [Function: set],  ??????

//   enumerable: true,

//   configurable: true 

// }

// Notice that getters and setters are present in the ElectricCar2 object for 'discount' property!

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

這是一個小的更新,它容許咱們在函數最後一個參數的後面添加尾隨逗號。爲何這麼作?它能夠幫助像git blame這樣的工具確認全部的責任都是新的開發者引發的。

下面的示例說明這個問題和解決方法。

圖片描述

注意:你能夠尾隨逗號來調用函數。

6. Async/Await

若是你問我,我會說這個迄今爲止最重要也是最有用的功能。異步函數容許們不再用處理回調地獄,同時使用整個代碼看起來很簡單。

async關鍵字告訴JavaScript編譯器,以不一樣的方式處理這個函數。在函數內執行到await關鍵字時,編譯器將會暫停。它假設在等待以後的表達式返回一個承諾,並等待承諾解決或拒絕後繼續執行。

在這個示例中,getAmount函數將調用兩個異步的函數getUser 和 getBankBalance。咱們能夠經過Promise實現,但使用async await會更加的優雅和簡單。.

圖片描述

6.1 異步函數自己返回一個Promise.
若是你想等待一個異步函數的返回結果,你須要你須要使用Promise’s then語法來捕獲返回結果。

在下面的示例中,咱們想經過console.log來打印返回結果,而不是在doubleAndAdd函數內部。因此咱們想等函數返回結果並經過then語法將結果傳遞給console.log 。

圖片描述

6.2 並行調用async/await
在上一個示例中,咱們調用了兩次await語句,每一次等待1s(共2s)。In the previous example we are calling await twice, but each time we are waiting for one second (total 2 seconds). 相反,咱們可使用Promise.all並行執行兩次調用,由於a和b。

圖片描述

6.3 async/await函數的錯誤處理
使用async await時,有各類方法來處理錯誤。

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

//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);

 });

}

Option 2— 捕獲每一個一個 await 表達式
由於每個await表達式都返回一個Promise,你能夠像下面展現在每一行上捕獲錯誤。

//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);

 });

}

Option 3 — 捕獲整個 async-await 函數

//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年6月或7月發佈。 is currently in final draft and will be out in June or July 2018. 下文函數的全部功能都將在Stage-4中,並將成爲ECMAScript 2018的一部分。

1. 共享內存和原子

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

它的主要思想是想爲JS提供一些多線程的功能,以便JS開發人員能夠本身管理內存(代替JS引擎對內存的管理)來編寫高性能的併發程序。

這是經過一種叫作SharedArrayBuffer的全局對象來實現的,它實質上是將數據存儲在_共享_ 內存空間中。因此這個數據能夠在JS主線程和web-worker之間共享。

到如今爲止,若是咱們想在JS主線程和web-workers之間共享數據,只能先複製數據而後使用postMessage 發送到其它線程。

如今,只須要使用SharedArrayBuffer,主線程和多個web工做線程便可訪問共享數據。

可是在線程之間共享內存會引起競爭條件的產生。爲了防止條件的產生引入了「Atomics」全局對象。 當一個線程正在使用它的數據時,Atomics 提供了各類方法來鎖定共享內存。 它還提供了安全地更新共享內存中的數據的方法。

建議經過某些庫來使用這些功能,可是如今尚未構建在此功能上的庫。

若是你有興趣,推薦閱讀:

From Workers to Shared Memor_y — _lucasfcosta
A cartoon intro to SharedArrayBuffers_ — _Lin Clark
Shared memory and atomics_ — _Dr. Axel Rauschmayer

2. 刪除標籤模板字符的限制

首先,咱們要弄清楚「Tagged Template literal」是什麼,這樣咱們才能更好的理解這個特性。

在ES2015+中,有一個叫標籤模板的功能,它容許開發人員自定義如何插入字符串。例如,以標籤方式插入字符串,以下所示... ...
圖片描述

在標籤中,能夠寫一個函數來接收字符串常量部分,如[ ‘Hello ‘, ‘!’ ]和要替換的變量,如[ 'Raja'], 把它們做爲參數傳入自定義的函數(如greet),而後從這個自定義的函數中返回任何所須要的內容。

下面的示例展現了咱們自定義的標籤函數greet給某個時間附加如「Good Morning!」、「Good afternoon」這些字符串,取決於某個具體的時間並返回一個自定義的字符串。

圖片描述

//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!';

}

如今咱們討論被看成標識名的函數究竟是什麼,許多人但願在不一樣的場景中使用這個功能,例如在終端命令和HTTP請求的URI構成中,等。

⚠️The problem with Tagged String literal
問題是在ES2015和ES2016規範中不容許使用`「u」 (unicode),「x」(hexadecimal) 等轉義字符,除非它們看起來徹底像\u00A9 或 u{2F804} 或 xA9。

所以,若是你在一個標記函數內使用其它域的規則(如終端規則),那麼可能須要使用ubla123abla這種規則,它看起來不像u0049 或 u{@F804},而後你將會獲得一個語法錯誤。

在ES2018中,使用規則是很是寬鬆的,甚至無效的轉義字符。只要標記函數經過對象返回屬性值,如「cooked」(無效字符串是「undefined」)和「raw」(包括任何其它你想要的屬性)。

function myTagFunc(str) { 

 return { "cooked": "undefined", "raw": str.raw[0] }

} 


var str = myTagFunc `hi \
  1. 正則表達式的「dotall」標誌

在如今的RegEx中,儘管(「.」)應該匹配單個字符,便它不匹配會產生新的字符,像n r f等。

例如:

//Before

/first.second/.test('first\nsecond');
//false

這個加強的功能可使點運算符匹配任何單符。爲了確保不破壞任何內容,咱們須要使用s標記來建立RegEx表示以使其能正常工做。

//ECMAScript 2018

/first.second/s.test('first\nsecond');
//true   Notice: /s ??

如下是(提案)[https://github.com/tc39/propo...] 文檔中的所有API:

圖片描述

4. RegEx命名的組捕獲 ?

這個加強的功能模仿其它語言,如Python,Java等中帶來一個很是有用的特性叫「命名組」。此特性容許開發人員編寫RegExp時,爲RegExp中的組的不一樣部分提供格式爲「(<name>...)」的名稱(標識符)。 而後,他們可使用該名稱輕鬆獲取他們須要的任何組。

4.1 基本的命名組示例
在下面的示例中,咱們使用(?<year>) (?<month>) and (?year)名稱對日期RegEx的不一樣部分進行分組。 結果對象如今將包含一個具groups屬性,它有year,month和year屬性及相應的值。

4.2 在正則表達式自己內使用命名組
咱們可使用k <組名>這種格式在正則表達式自己反向引用該組。 下面的例子展現它的工做原理:

4.3 在String.prototype.replace中使用命名組
如今,命名組這個特性已經能夠在字符串的實例方法replace中使用了,因此咱們能夠很容易地替換字符串的單詞。

例如,把「firstName, lastName」替換成「lastName, firstName」。

5. 對象的解構屬性

解構操做符 ... (三個...)容許咱們提取對象中沿未提取的屬性。

5.1 你可使用解構來只提取你想要的屬性

5.2 更方便的是你能夠刪除你不想要的屬 ??

6. 對象的屬性擴展

擴展屬性也是使用三個點 ...,解構屬性,不一樣的是使用擴展建立(從新構建)的是新的對象。

提醒:展開運算符用於等號的右側,解構運算符用於等號的左側。

7. RegExp Lookbehind Assertions

這是RegEx的一個加強的功能,它可使咱們確保某些字符串必定在其它的字符中以前。

您如今可使用一個組(?<= ...) (問號,小於,等於)來查找確定的斷言。

此外,您可使用(?<!...)(問號,小於,感嘆號)來查看否認斷言。從本質上講,只要-ve斷言經過,就會匹配。

確定的斷言: 好比說,咱們但願確保#在單詞winning以前(即:#winning),並但願正則表達之返回字符串「winning」,下面展現它的寫法。

否認的斷言: 比方說,咱們但願獲取€而不是$字符以後的數字。

8. RegExp Unicode 轉義

要編寫RegEx來匹配各類Unicode字符是很難的。諸如 w, W, d等只能匹配英文字符和數字。 可是對於印度語,希臘語等其餘語言的數字呢?

這就是Unicode Property Escapes的用武之地。結果是,Unicode爲每一個符號(字符)添加了元數據屬性,並使用它來對各類符號進行分組或表徵。

例如,Unicode數據庫將全部印地語字符(हिन्दी)分組在一個key爲Script、值爲Devanagari的屬性下,另外一個key爲Script_Extensions的屬性值也爲Devanagari。因此咱們能夠搜索Script = Devanagari並得到全部的印地文字符。

` Devanagari 可用於馬拉地語,北印度語,梵語等各類印度語言。

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

//The following matches multiple hindi character

/^p{Script=Devanagari}+$/u.test('हिन्दी');
//true

//PS:there are 3 hindi characters h
一樣,Unicode數據庫將全部希臘字符組合爲Script_Extensions(和Script)屬性,其值爲Greek。 因此咱們可使用Script_Extensions = Greek或Script = Greek來搜索全部希臘字符。

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

//The following matches a single Greek character

/p{Script_Extensions=Greek}/u.test('π'); // true
此外,Unicode數據庫將各類類型的表情符號存儲在屬性值爲「真」的布爾屬性Emoji,Emoji_Component,Emoji_Presentation,Emoji_Modifier和Emoji_Modifier_Base下。因此咱們能夠經過簡單的選擇「Emoji」來搜索全部的表情符號。

也就是說,咱們可使用: p {Emoji} , Emoji_Modifier 等來匹配各類表情符號。

下面的例子將會說明的更清晰一點。

//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)來否認匹配。

參考:
ECMAScript 2018 Proposal
https://mathiasbynens.be/note...

  1. Promise.prototype.finally()

finally() 是Promise新添加的實例方法。主要想法是容許在「resolve」或「reject」以後運行回調來處理其它邏輯。 finally **回調被調用時沒有任何價值,而且不管如何老是被執行。

看看不一樣的狀況。

9. 異步迭代

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

這個特性增長了一個新的「for-await-of」循環,容許咱們調用異步函數來返回承諾(或帶有一系列承諾的數組)。比較的酷的事情是,在下一個循環執行以前當前循環都會等待全部Promise返回承諾。
圖片描述

相關文章
相關標籤/搜索