ES8、ES9和ES10新特徵梳理

相信很多人已經用過ES6或者ES7的部分新特性,比如class、promise、解構等等,也對ES6、ES7比較熟悉,網上也有較多的文章是關於這兩個標準的,所以小編就不再贅述了,那今天給大夥帶來的主要是關於ES8、ES9和ES10的部分新特性。

ES8、ES9和ES10新特徵梳理

ES7新特性(2016)

ES2016添加了兩個小的特性來說明標準化過程:

  • 數組includes()方法,用來判斷一個數組是否包含一個指定的值,根據情況,如果包含則返回true,否則返回false。
  • a ** b指數運算符,它與 Math.pow(a, b)相同。

1.Array.prototype.includes()

includes() 函數用來判斷一個數組是否包含一個指定的值,如果包含則返回 true,否則返回false。

includes 函數與 indexOf 函數很相似,下面兩個表達式是等價的:

arr.includes(x)
arr.indexOf(x) >= 0

接下來我們來判斷數字中是否包含某個元素:

//在ES7之前的做法,使用indexOf()驗證
let arr = ['react', 'angular', 'vue'];
if (arr.indexOf('react') !== -1)
{
console.log('react存在');
}

//使用ES7的includes()

let arr = ['react', 'angular', 'vue'];
if (arr.includes('react'))
{
console.log('react存在');
}

2.指數操作符

在ES7中引入了指數運算符**,**具有與Math.pow(..)等效的計算結果。

不使用指數操作符

使用自定義的遞歸函數calculateExponent或者Math.pow()進行指數運算:


//ES7以前使用自定義的遞歸函數calculateExponent或者Math.pow()進行指數運算
function calculateExponent(base, exponent)
{
if (exponent === 1)
{
return base;
}
else
{
return base * calculateExponent(base, exponent - 1);
}
}
console.log(calculateExponent(2, 10)); // 輸出1024
console.log(Math.pow(2, 10)); // 輸出1024
複製代碼

//ES7使用指數操作符
console.log(2**10);// 輸出1024

可以看出指數操作符可以像+、-一樣使用,而且寫法更加簡潔。

ES8新特性(2017)

ES8梳理的主要內容目錄如下:

  • Object.values()
  • Object.entries()
  • String padding: padStart()和padEnd(),填充字符串達到當前長度
  • 函數參數列表結尾允許逗號
  • Object.getOwnPropertyDescriptors()
  • ShareArrayBuffer和Atomics對象,用於從共享內存位置讀取和寫入

1.Object.values()

Object.values()是一個與Object.keys()類似的新函數,但返回的是Object自身屬性的所有值,不包括繼承的值。

假設我們要遍歷如下對象obj的所有值:

const obj = {a: 1, b: 2, c: 3};

//不使用ES7的Object.values()方法
const vals=Object.keys(obj).map(key=>obj[key]);
console.log(vals);//[1, 2, 3]

//使用Object.values()方法
const values=Object.values(obj1);
console.log(values);//[1, 2, 3]

從上述代碼中可以看出Object.values()為我們省去了遍歷key,並根據這些key獲取value的步驟。

2.Object.entries()

Object.entries()函數返回一個給定對象自身可枚舉屬性的鍵值對的數組。

接下來我們來遍歷上文中的obj對象的所有屬性的key和value:

const obj = {a: 1, b: 2, c: 3};

//不使用Object.entries()
Object.keys(obj).forEach(key=>{
\tconsole.log('key:'+key+' value:'+obj[key]);
})
//key:a value:1
//key:b value:2
//key:c value:3

//使用Object.entries()
for(let [key,value] of Object.entries(obj1)){
\tconsole.log(`key: ${key} value:${value}`)
}
//key:a value:1
//key:b value:2
//key:c value:3

3.String padding

在ES8中String新增了兩個實例函數String.prototype.padStart和String.prototype.padEnd,允許將空字符串或其他字符串添加到原始字符串的開頭或結尾。

String.padStart(targetLength,[padString])
  • targetLength:當前字符串需要填充到的目標長度。如果這個數值小於當前字符串的長度,則返回當前字符串本身。
  • padString:(可選)填充字符串。如果字符串太長,使填充後的字符串長度超過了目標長度,則只保留最左側的部分,其他部分會被截斷,此參數的缺省值為 " "。
//使用示例
console.log('0.0'.padStart(4,'10')) //10.0
console.log('0.0'.padStart(20))// 0.00
String.padEnd(targetLength,padString])
  • targetLength:當前字符串需要填充到的目標長度。如果這個數值小於當前字符串的長度,則返回當前字符串本身。
  • padString:(可選) 填充字符串。如果字符串太長,使填充後的字符串長度超過了目標長度,則只保留最左側的部分,其他部分會被截斷,此參數的缺省值為 " ";
//使用示例
console.log('0.0'.padEnd(4,'0')) //0.00
console.log('0.0'.padEnd(10,'0'))//0.00000000

4.函數參數列表結尾允許逗號

主要作用是方便使用git進行多人協作開發時修改同一個函數減少不必要的行變更。

5.Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors()函數用來獲取一個對象的所有自身屬性的描述符,如果沒有任何自身屬性,則返回空對象。

const obj2 = {
\tname: 'zou',
\tget age() { return '18' }
};
Object.getOwnPropertyDescriptors(obj2)
// {
// age: {
// configurable: true,
// enumerable: true,
// get: function age(){}, //the getter function
// set: undefined
// },
// name: {
// configurable: true,
// enumerable: true,
//\t\tvalue:"zou",
//\t\twritable:true
// }
// }

6.SharedArrayBuffer對象

SharedArrayBuffer 對象用來表示一個通用的,固定長度的原始二進制數據緩衝區,類似於 ArrayBuffer 對象,它們都可以用來在共享內存(shared memory)上創建視圖。與 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分離。

/**
*
* @param {*} length 所創建的數組緩衝區的大小,以字節(byte)為單位。
* @returns {SharedArrayBuffer} 一個大小指定的新 SharedArrayBuffer 對象。其內容被初始化為 0。

*/
new SharedArrayBuffer(length)

7.Atomics對象

Atomics 對象提供了一組靜態方法用來對 SharedArrayBuffer 對象進行原子操作。

這些原子操作屬於 Atomics 模塊。與一般的全局對象不同,Atomics 不是構造函數,因此不能使用 new 操作符調用,也不能將其當作函數直接調用。Atomics 的所有屬性和方法都是靜態的(與 Math 對象一樣)。

多個共享內存的線程能夠同時讀寫同一位置上的數據。原子操作會確保正在讀或寫的數據的值是符合預期的,即下一個原子操作一定會在上一個原子操作結束後才會開始,其操作過程不會中斷。

Atomics.add()
將指定位置上的數組元素與給定的值相加,並返回相加前該元素的值。
Atomics.and()
將指定位置上的數組元素與給定的值相與,並返回與操作前該元素的值。
Atomics.compareExchange()
如果數組中指定的元素與給定的值相等,則將其更新為新的值,並返回該元素原先的值。
Atomics.exchange()
將數組中指定的元素更新為給定的值,並返回該元素更新前的值。

Atomics.load()
返回數組中指定元素的值。
Atomics.or()
將指定位置上的數組元素與給定的值相或,並返回或操作前該元素的值。
Atomics.store()
將數組中指定的元素設置為給定的值,並返回該值。
Atomics.sub()
將指定位置上的數組元素與給定的值相減,並返回相減前該元素的值。
Atomics.xor()
將指定位置上的數組元素與給定的值相異或,並返回異或操作前該元素的值。

wait() 和 wake() 方法採用的是 Linux 上的 futexes 模型(fast user-space mutex,快速用戶空間互斥量),可以讓進程一直等待直到某個特定的條件為真,主要用於實現阻塞。

Atomics.wait()
檢測數組中某個指定位置上的值是否仍然是給定值,是則保持掛起直到被喚醒或超時。返回值為 "ok"、"not-equal" 或 "time-out"。調用時,如果當前線程不允許阻塞,則會拋出異常(大多數瀏覽器都不允許在主線程中調用 wait())。
Atomics.wake()
喚醒等待隊列中正在數組指定位置的元素上等待的線程。返回值為成功喚醒的線程數量。
Atomics.isLockFree(size)

可以用來檢測當前系統是否支持硬件級的原子操作。對於指定大小的數組,如果當前系統支持硬件級的原子操作,則返回 true;否則就意味著對於該數組,Atomics 對象中的各原子操作都只能用鎖來實現。此函數面向的是技術專家。-->
ES8、ES9和ES10新特徵梳理

ES9新特性(2018)

ES9梳理的主要內容目錄如下:

  • 異步迭代
  • Promise.finally()
  • Rest/Spread 屬性
  • 正則表達式命名捕獲組(Regular Expression Named Capture Groups)
  • 正則表達式反向斷言(lookbehind)
  • 正則表達式dotAll模式
  • 正則表達式 Unicode 轉義
  • 非轉義序列的模板字符串

1.異步迭代

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

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()

一個Promise調用鏈要麼成功到達最後一個.then(),要麼失敗觸發.catch()。在某些情況下,你想要在無論Promise運行成功還是失敗,運行相同的代碼,例如清除,刪除對話,關閉數據庫連接等。

.finally()允許你指定最終的邏輯:

function doSomething() {
doSomething1()
.then(doSomething2)
.then(doSomething3)
.catch(err => {
console.log(err);
})
.finally(() => {
// finish here!

});
}
複製代碼

3.Rest/Spread 屬性

ES2015引入了Rest參數和擴展運算符。三個點(...)僅用於數組。Rest參數語法允許我們將一個布丁數量的參數表示為一個數組。

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

展開操作符以相反的方式工作,將數組轉換成可傳遞給函數的單獨參數。例如Math.max()返回給定數字中的最大值:

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

ES2018為對象解構提供了和數組一樣的Rest參數()和展開操作符,一個簡單的例子:

const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }

或者你可以使用它給函數傳遞參數:

restParam({
a: 1,
b: 2,
c: 3
});
function restParam({ a, ...x }) {
// a = 1
// x = { b: 2, c: 3 }
}

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

擴展運算符可以在其他對象內使用,例如:

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

可以使用擴展運算符拷貝一個對象,像是這樣obj2 = {...obj1},但是 這只是一個對象的淺拷貝。另外,如果一個對象A的屬性是對象B,那麼在克隆後的對象cloneB中,該屬性指向對象B。

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

JavaScript正則表達式可以返回一個匹配的對象——一個包含匹配字符串的類數組,例如:以YYYY-MM-DD的格式解析日期:

const
reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
match = reDate.exec('2018-04-30'),
year = match[1], // 2018
month = match[2], // 04
day = match[3]; // 30

這樣的代碼很難讀懂,並且改變正則表達式的結構有可能改變匹配對象的索引。

ES2018允許命名捕獲組使用符號?<name>,在打開捕獲括號(後立即命名,示例如下:/<name>

const
reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?[0-9]{2})/,
match = reDate.exec('2018-04-30'),
year = match.groups.year, // 2018
month = match.groups.month, // 04
day = match.groups.day; // 30
/<month>/<year>

任何匹配失敗的命名組都將返回undefined。

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

const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?[0-9]{2})/,
d = '2018-04-30',
usDate = d.replace(reDate, '$<month>-$-$<year>');
/<year>
/<month>
/<month>/<year>

5.正則表達式反向斷言

目前JavaScript在正則表達式中支持先行斷言(lookahead)。這意味著匹配會發生,但不會有任何捕獲,並且斷言沒有包含在整個匹配字段中。例如從價格中捕獲貨幣符號:

const reLookahead = /\\D(?=\\d+)/,
match = reLookahead.exec('$123.89');
console.log( match[0] ); // $

ES2018引入以相同方式工作但是匹配前面的反向斷言(lookbehind),這樣我就可以忽略貨幣符號,單純的捕獲價格的數字:

const reLookbehind = /(?<=\\D)\\d+/,
match = reLookbehind.exec('$123.89');
console.log( match[0] ); // 123.89

以上是 肯定反向斷言,非數字\\D必須存在。同樣的,還存在 否定反向斷言,表示一個值必須不存在,例如:

const reLookbehindNeg = /(?match = reLookbehind.exec('$123.89');
console.log( match[0] ); // null

6.正則表達式dotAll模式

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

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

7.正則表達式 Unicode 轉義

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

const reGreekSymbol = /\\p{Script=Greek}/u;
reGreekSymbol.test('π'); // true

此特性可以避免使用特定 Unicode 區間來進行內容類型判斷,提升可讀性和可維護性。

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

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

ES8、ES9和ES10新特徵梳理

ES10新特性(2019)

ES10梳理的主要內容目錄如下:

  • 行分隔符(U + 2028)和段分隔符(U + 2029)符號現在允許在字符串文字中,與JSON匹配
  • 更加友好的 JSON.stringify
  • 新增了Array的flat()方法和flatMap()方法
  • 新增了String的trimStart()方法和trimEnd()方法
  • Object.fromEntries()
  • Symbol.prototype.description
  • String.prototype.matchAll
  • Function.prototype.toString()現在返回精確字符,包括空格和註釋
  • 簡化try {} catch {},修改 catch 綁定
  • 新的基本數據類型BigInt
  • globalThis
  • import()
  • Legacy RegEx
  • 私有的實例方法和訪問器

1.行分隔符(U + 2028)和段分隔符(U + 2029)符號現在允許在字符串文字中,與JSON匹配

以前,這些符號在字符串文字中被視為行終止符,因此使用它們會導致SyntaxError異常。

2.更加友好的 JSON.stringify

如果輸入 Unicode 格式但是超出範圍的字符,在原先JSON.stringify返回格式錯誤的Unicode字符串。現在實現了一個改變JSON.stringify的第3階段提案,因此它為其輸出轉義序列,使其成為有效Unicode(並以UTF-8表示)

3.新增了Array的flat()方法和flatMap()方法

flat()和flatMap()本質上就是是歸納(reduce) 與 合併(concat)的操作。

Array.prototype.flat()

flat() 方法會按照一個可指定的深度遞歸遍歷數組,並將所有元素與遍歷到的子數組中的元素合併為一個新數組返回。

  • flat()方法最基本的作用就是數組降維
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];

arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity 作為深度,展開任意深度的嵌套數組
arr3.flat(Infinity);
// [1, 2, 3, 4, 5, 6]
  • 其次,還可以利用flat()方法的特性來去除數組的空項
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]

Array.prototype.flatMap()

flatMap() 方法首先使用映射函數映射每個元素,然後將結果壓縮成一個新數組。它與 map 和 深度值1的 flat 幾乎相同,但 flatMap 通常在合併成一種方法的效率稍微高一些。 這裡我們拿map方法與flatMap方法做一個比較。

var arr1 = [1, 2, 3, 4];
arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
// 只會將 flatMap 中的函數返回的數組 “壓平” 一層
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

4.新增了String的trimStart()方法和trimEnd()方法

新增的這兩個方法很好理解,分別去除字符串首尾空白字符,這裡就不用例子說聲明瞭。

5.Object.fromEntries()

Object.entries()方法的作用是返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與使用 for...in 循環遍歷該對象時返回的順序一致(區別在於 for-in 循環也枚舉原型鏈中的屬性)。

Object.fromEntries() 函數傳入一個鍵值對的列表,並返回一個帶有這些鍵值對的新對象。這個迭代參數應該是一個能夠實現@iterator方法的的對象,返回一個迭代器對象。它生成一個具有兩個元素的類似數組的對象,第一個元素是將用作屬性鍵的值,第二個元素是與該屬性鍵關聯的值。

  • 通過 Object.fromEntries, 可以將 Map 轉化為 Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
  • 通過 Object.fromEntries, 可以將 Array 轉化為 Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);

console.log(obj); // { 0: "a", 1: "b", 2: "c" }

6.Symbol.prototype.description

通過工廠函數Symbol()創建符號時,您可以選擇通過參數提供字符串作為描述:

const sym = Symbol('The description');

以前,訪問描述的唯一方法是將符號轉換為字符串:

assert.equal(String(sym), 'Symbol(The description)');

現在引入了getter Symbol.prototype.description以直接訪問描述:

assert.equal(sym.description, 'The description');

7.String.prototype.matchAll

matchAll() 方法返回一個包含所有匹配正則表達式及分組捕獲結果的迭代器。 在 matchAll 出現之前,通過在循環中調用regexp.exec來獲取所有匹配項信息(regexp需使用/g標誌:

const regexp = RegExp('foo*','g');
const str = 'table football, foosball';
while ((matches = regexp.exec(str)) !== null) {
console.log(`Found ${matches[0]}. Next starts at ${regexp.lastIndex}.`);
// expected output: "Found foo. Next starts at 9."
// expected output: "Found foo. Next starts at 19."
}

如果使用matchAll ,就可以不必使用while循環加exec方式(且正則表達式需使用/g標誌)。使用matchAll 會得到一個迭代器的返回值,配合 for...of, array spread, or Array.from() 可以更方便實現功能:

const regexp = RegExp('foo*','g'); 
const str = 'table football, foosball';
let matches = str.matchAll(regexp);
for (const match of matches) {
console.log(match);
}
// Array [ "foo" ]
// Array [ "foo" ]
// matches iterator is exhausted after the for..of iteration
// Call matchAll again to create a new iterator
matches = str.matchAll(regexp);
Array.from(matches, m => m[0]);
// Array [ "foo", "foo" ]

matchAll可以更好的用於分組

var regexp = /t(e)(st(\\d?))/g;
var str = 'test1test2';
str.match(regexp);
// Array ['test1', 'test2']

let array = [...str.matchAll(regexp)];
array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]

8.Function.prototype.toString()現在返回精確字符,包括空格和註釋

function /* comment */ foo /* another comment */() {}
// 之前不會打印註釋部分
console.log(foo.toString()); // function foo(){}
// ES2019 會把註釋一同打印
console.log(foo.toString()); // function /* comment */ foo /* another comment */ (){}
// 箭頭函數
const bar /* comment */ = /* another comment */ () => {};
console.log(bar.toString()); // () => {}

9.修改 catch 綁定

在 ES10 之前,我們必須通過語法為 catch 子句綁定異常變量,無論是否有必要。很多時候 catch 塊是多餘的。 ES10 提案使我們能夠簡單的把變量省略掉。

不算大的改動。

之前是

try {} catch(e) {}

現在是

try {} catch {}

10.新的基本數據類型BigInt

現在的基本數據類型(值類型)不止5種(ES6之後是六種)了哦!加上BigInt一共有七種基本數據類型,分別是: String、Number、Boolean、Null、Undefined、Symbol、BigInt

小結

以上就是關於ES8、ES9和ES10的一些新特徵描述,可能內容比較多,需要耐心的理解和實踐,希望大夥都能多學多做,不斷的追求更新更好的技術。


分享到:


相關文章: