ECMAScript10(ES10)功能完全指南

ES10仍然只是一個草案。但是除了Object.fromEntries大多數功能已經在Chrome中實現,所以你爲什麼不盡早開始探索它呢?當所有瀏覽器開始支持它時,你已經獲得了領先優勢,這只是時間問題。對於有興趣探索ES10的人來說,這是一份非外星人指南。

ES10在新語言功能方面沒有ES6那麼重要,但它確實添加了一些有趣的東西(其中一些在目前版本的瀏覽器中還不起作用:02/20/2019)

ES6中最受歡迎的功能莫過於箭頭函數了,那麼ES10中呢?

BigInt - 任意精度整數

BigInt是第7種原始類型。

BigInt是一個任意精度的整數。這意味着變量現在可以代表2^53個數字。而且最大限度是9007199254740992。

const b = 1n; //追加n來創建一個BigInt

在過去的整數值大於9007199254740992不支持。如果超出,則該值將鎖定爲MAX_SAFE_INTEGER + 1

const limit = Number.MAX_SAFE_INTEGER;9007199254740991
limit + 1;9007199254740992
limit + 2;9007199254740992 <--- MAX_SAFE_INTEGER + 1 exceeded
const larger = 9007199254740991n;9007199254740991n
const integer = BigInt(9007199254740991); // initialize with number9007199254740991n
const same = BigInt("9007199254740991"); // initialize with "string"9007199254740991n

typeof

typeof 10;'number'
typeof 10n;'bigint'

=== ==

10n === BigInt(10);true
10n == 10;true

* /

200n / 10n
⇨ 20n
200n / 20
⇨ Uncaught TypeError:
   Cannot mix BigInt and other types, use explicit conversions <

- +

-100n
⇨ -100n
+100n
⇨ Uncaught TypeError:
  Cannot convert a BigInt value to a number

當你讀到這個matchAll時,它可能會在Chrome C73中正式實現 - 如果沒有,它仍然值得一看。特別是如果你是一個正則表達式癮君子。

string.prototype.matchAll()

如果你谷歌搜索"javascript string match all",第一條結果可能會是這樣的How do I write a regular expression to “match all”? 。 排名靠前的結果會建議你使用String.match匹配的時候在正則表達式或者RegExp.exc或者RegExp.text後加上/g

首先,我們來看下舊的規範是如何運行的。

String.matchmatch只返回字符串參數第一個符合匹配的。

let string = 'Hello'
let matches = string.match('l')
console.log(matches[0]) // 'l'

匹配的結果是單個'l'。(注意: match匹配的結果存儲在matches[0]而非在matches),在字符串'hello'中搜索匹配'l'只有'l'被返回來。使用regexp參數也是得到一樣的結果。

我們把字符'l'更換爲表達式/l/:

let string = 'Hello'
let matches = string.match(/l/)
console.log(matches[0]) // 'l'

添加 /g

String.match使用正則表達式帶上/g標籤會返回多個匹配。

let string = 'Hello'
let ret = string.match(/l/g) // ['l', 'l']

Great…在低於ES10的環境中我們得到了多個匹配結果,並且一直有效。

那麼爲什麼要用全新的matchAll方法呢?在我們更詳細地回答這個問題之前,讓我們來看看capture group。如果不出意外,你可能會學到新的有關正則表達式的東西。

正則表達式捕獲組

在正則表達式中捕獲組只是在()括號中提取匹配。你可以從/regex/.exec(string)string.match捕獲組。

通常捕獲組是在匹配規則中被創建的。輸出對象上創建groups屬性如: (?<name>)。要創建一個新的組名,只需在括號內添加 (?<name>)屬性,分組(模式)匹配將成爲附加到match對象的groups.name

看一個實際的例子:

字符串標本匹配


創建match.groups.color & match.groups.bird匹配:

const string = 'black*raven lime*parrot white*seagull'
const regex = /(?<color>.*?)\*(?<bird>[a-z0-9]+)/g
while (match = regex.exec(string) {
    let value = match[0]
    let index = match.index
    let input = match.input
    console.log(`${value} at ${index} with '${input}'`)
    console.log(match.groups.color)
    console.log(match.groups.bird)
}

需要多次調用regex.exec方法來遍歷整個搜索結果。在每次迭代調用.exec時,會顯示下一個結果(它不會立即返回所有匹配項)。

控制檯輸出:

black*raven at 0 with 'black*raven lime*parrot white*seagull'
black
raven
lime*parrot at 11 with 'black*raven lime*parrot white*seagull'
lime
parrot
white*seagull at 23 with 'black*raven lime*parrot white*seagull'
white
seagull

這裏有一個怪事:

如果你從這個正則表達式中刪除/ g,你將永遠在第一個結果上創建一個無限循環循環。這在過去是一個巨大的痛苦。想象一下從某個數據庫接收正則表達式,你不確定它是否在最後有/ g。你必須先檢查它,等等。

現在我們有足夠的背景知識回答這個問題:

最好使用 .matchAll()

  1. 使用捕獲組時更加優雅。捕獲組知識帶有提取模式()的正則表達式的一部分。
  2. 它返回一個迭代器而不是數組,迭代器本身很有用。
  3. 可以使用擴展運算符…迭代器轉爲數組
  4. 避免使用帶/g標誌的正則表達式…當從數據庫或外部源檢索未知的正則表達式並與古老的RegEx對象一起使用時非常有用。
  5. 使用RegExp對象創建的正則表達式不能使用點(.)運算符鏈接。
  6. **高級:RegEx**對象跟蹤最後匹配位置的內部.lastIndex屬性,這可能對複雜案例有破壞性的事情。

.matchAll()如何工作

這是一簡單個例子。

我們嘗試匹配字符串Hello的所有el。因爲返回了iterator,所以我們用for ... of處理它。

// Match all occurrences of the letters: 'e' 或者 'l'
let iterator = 'hello'.matchAll(/[el]/)
for (const match of iterator) {
    console.log(match)
}

如上,你可以跳過/g.matchAll不需要它。結果:

[ 'e', index: 1, input: 'hello' ] // Iteration 1
[ 'l', index: 2, input: 'hello' ] // Iteration 2
[ 'l', index: 3, input: 'hello' ] // Iteration 3

使用.matchAll()捕獲組示例:
.matchAll()具有上面列舉的所有好處,它是一個迭代器,所以我們可以用它來循環,這就是整個句法差異。

const string = 'black*raven lime*parrot white*seagull';
const regex = /(?<color>.*?)\*(?<bird>[a-z0-9]+)/;
for (const match of string.matchAll(regex)) {
    let value = match[0];
    let index = match.index;
    let input = match.input;
    console.log(`${value} at ${index} with '${input}'`);
    console.log(match.groups.color);
    console.log(match.groups.bird);
}

注意去掉/g標誌,因爲.matchAll()已經隱含了它。

結果輸出:

black*raven at 0 with 'black*raven lime*parrot white*seagull'
black
raven
lime*parrot at 11 with 'black*raven lime*parrot white*seagull'
lime
parrot
white*seagull at 23 with 'black*raven lime*parrot white*seagull'
white
seagull

也許在美學上它與循環實現時的原始regex.exec非常相似。但如前所述,由於上述許多原因,這是更好的方法。並且刪除/ g不會導致無限循環。

動態 import

現在可以將導入分配給一個變量:

element.addEventListener('click', async () => {
    const module = await import('./api-scripts/button-click.js')
    module.clickEvent()
})

Array.flat()

扁平化多維數組:

let multi = [1,2,3,[4,5,6,[7,8,9,[10,11,12]]]];
multi.flat();               // [1,2,3,4,5,6,Array(4)]
multi.flat().flat();        // [1,2,3,4,5,6,7,8,9,Array(3)]
multi.flat().flat().flat(); // [1,2,3,4,5,6,7,8,9,10,11,12]
multi.flat(Infinity);       // [1,2,3,4,5,6,7,8,9,10,11,12]

Array.flatMap()

let array = [1, 2, 3, 4, 5]
array.map(x => [x, x * 2])

變爲:

[Array(2), Array(2), Array(2)]
0: (2)[1, 2]
1: (2)[2, 4]
2: (2)[3, 6]
3: (2)[4, 8]
4: (2)[5, 10]

再次扁平化數組:

array.flatMap(v => [v, v * 2])
[1, 2, 2, 4, 3, 6, 4, 8, 5, 10]

Object.fromEntries()

將鍵值對列表轉換爲對象。

let obj = { apple : 10, orange : 20, banana : 30 };
let entries = Object.entries(obj);
entries;
(3) [Array(2), Array(2), Array(2)]
 0: (2) ["apple", 10]
 1: (2) ["orange", 20]
 2: (2) ["banana", 30]
let fromEntries = Object.fromEntries(entries);
{ apple: 10, orange: 20, banana: 30 }

String.trimStart() & String.trimEnd()

let greeting = "     Space around     ";
greeting.trimEnd();   // "     Space around";
greeting.trimStart(); // "Space around     ";

格式良好的JSON.stringify()

此更新修復了字符U + D800U + DFFF的處理,有時可以進入JSON字符串。這可能是一個問題,因爲JSON.stringify可能會返回格式化爲沒有等效UTF-8字符的值的這些數字。但JSON格式需要UTF-8編碼。

JSON 對象可用於解析JSON 格式(但也更多。)JavaScript JSON 對象也具有stringifyparse方法。

該解析方法適用於一個結構良好的JSON字符串,如:

'{ “prop1” : 1, "prop2" : 2 }'; // A well-formed JSON format string

請注意,創建具有正確JSON格式的字符串絕對需要使用圍繞屬性名稱的雙引號。缺少...或任何其他類型的引號將不會產生格式良好的JSON

'{ “prop1” : 1, "meth" : () => {}}'; // Not JSON format string

JSON 字符串格式是不同的,從對象文本 …它看起來幾乎相同,但可以使用任何類型的周圍屬性名稱的報價,還可以包括方法(JSON格式不允許的方法):

let object_literal = { property:1,meth:()=> {} };

無論如何,一切似乎都很好。第一個示例看起來合規。但它們也是簡單的例子,大部分時間都可以毫無障礙地工作!

U + 2028和U + 2029字符

這是捕獲。ES10之前的EcmaScript實際上並不完全支持JSON格式。在ES10之前的時代,不接受未轉義的行分隔符U + 2028和段落分隔符U + 2029字符:


U + 2029是行分隔符。


U + 2029是段落分隔符。有時它可能會潛入您的JSON格式字符串。

對於U + D800 - U + DFFF之間的所有字符也是如此

如果這些字符悄悄進入你的JSON格式的字符串(比如說來自數據庫記錄),你最終可能花費數小時試圖弄清楚爲什麼程序的其餘部分會產生解析錯誤。

所以,如果你傳遞的eval一個字符串,像“console.log(‘hello’)”這將執行JavaScript語句(試圖通過字符串實際代碼轉換。)這也類似於如何JSON.parse將處理您的JSON字符串。

穩定的Array.prototype.sort()

V8的先前實現對包含10個以上項的數組使用了不穩定的快速排序算法。

一個穩定的排序算法是當兩個具有相等鍵的對象在排序輸出中以與未排序輸入中出現的順序相同的順序出現時。

但現在已經不是這樣了。ES10提供穩定的陣列排序:

var fruit = [
    { name: "Apple",      count: 13, },
    { name: "Pear",       count: 12, },
    { name: "Banana",     count: 12, },
    { name: "Strawberry", count: 11, },
    { name: "Cherry",     count: 11, },
    { name: "Blackberry", count: 10, },
    { name: "Pineapple",  count: 10, }
];
// Create our own sort criteria function:
let my_sort = (a, b) => a.count - b.count;
// Perform stable ES10 sort:
let sorted = fruit.sort(my_sort);
console.log(sorted);

控制檯輸出(項目以相反的順序出現):

New Function.toString()

Funcitons是對象,每個對象都有個.toString()方法因爲它最初存在於Object.prototype.toString()。所有的objects(包括functions)都繼承至基於原型的類繼承。這意味着我們已經有了function.toString()方法了。

但是ES10進一步嘗試標準化所有對象和內置函數的字符串表示。以下新案例:

Classic example

function () { console.log('Hello there.'); }.toString();

控制檯輸出(字符串格式的函數體:)

⇨ function () { console.log('Hello there.'); }

以下是其它案例:

直接來自函數名

Number.parseInt.toString();
⇨ function parseInt() { [native code] }

綁定上下文

function () { }.bind(0).toString();
⇨ function () { [native code] }

內置可調用函數對象

Symbol.toString();
⇨ function Symbol() { [native code] }

動態生成的函數

Function().toString();
⇨ function anonymous() {}

動態生成的生成器 function*

function* () { }.toString();
⇨ function* () { }

prototype.toString

Function.prototype.toString.call({});
⇨ Function.prototype.toString requires that 'this' be a Function"

可選的Catch Binding

在過去,try / catch語句中的catch子句需要一個變量。

try / catch語句幫助我們攔截在終端層面的錯誤:

這是一個複習:

try {
    // Call a non-existing function undefined_Function
    undefined_Function("I'm trying");
}
catch(error) {
    // Display the error if statements inside try above fail
    console.log( error ); // undefined_Function is undefined
}

但在某些情況下,所需的error變量未被使用:

try {
    JSON.parse(text); // <--- this will fail with "text not defined"
    return true; <--- exit without error even if there is one
}
catch (redundant_sometmes) <--- this makes error variable redundant
{
    return false;
}

編寫此代碼的人嘗試通過強制爲true退出try子句。但是…事實並非如此(正如 Douglas Massolari.所說)。

(() => {
    try {
        JSON.parse(text)
        return true
    } catch(err) {
        return false
    }
})()
=> false

在ES10中,Catch Error Binding是可選的

你現在可以跳過error變量:

try {
    JSON.parse(text);
    return true;
}
catch
{
    return false;
}

標準化的 globalThis 對象

ES10之前全局this沒有標準化。

生產代碼中,你必須手動添加如下代碼來標準化多個平臺的全局對象。

var getGlobal = function () {
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
};

但即使這樣也並不總是奏效。所以ES10添加了globalThis對象,從現在開始應該在任何平臺上訪問全局作用域:

// Access global array constructor
globalThis.Array(0, 1, 2);
⇨ [0, 1, 2]

// Similar to window.v = { flag: true } in <= ES5
globalThis.v = { flag: true };

console.log(globalThis.v);
⇨ { flag: true }

Symbol.description

description 是一個只讀屬性,返回Symbol 對象的可選描述。

let mySymbol = 'My Symbol';
let symObj = Symbol(mySymbol);
symObj; // Symbol(My Symbol)
String(symObj) === `Symbol(${mySymbol})`); // true
symObj.description; // "My Symbol"

Hashbang 語法

shebang unix用戶會熟悉AKA

它指定一個解釋器(什麼將執行您的JavaScript文件?)

ES10標準化了這一點。我不會詳細介紹這個,因爲這在技術上並不是一個真正的語言功能。但它基本上統一了JavaScript在服務器端的執行方式。

$ ./index.js

代替:

$ node index.js

在類Unix操作系統下。

ES10 Classes: private, static & public members

新的語法字符#(hash tag)現在直接在類主體作用域以及constructor和類方法裏被用來定義variablesfunctionsgetterssetters

這是一個相當無意義的示例,僅關注新語法:

class Raven extends Bird {
    #state = { eggs: 10};
    // getter
    get #eggs() { 
        return state.eggs;
    }
    // setter
    set #eggs(value) {
        this.#state.eggs = value;
    }
    #lay() {
        this.#eggs++;
    }
    constructor() {
        super();
        this.#lay.bind(this);
    }
    #render() {
        /* paint UI */
    }
}

說實話,我認爲它使語言更難閱讀。

它仍然是我最喜歡的新功能,因爲我喜歡C ++時代的classes

總結與反饋

ES10是一套尚未有機會在生產環境中進行全面探索的新功能。如果您有任何更正,建議或任何其他反饋,請告訴我們。

我經常寫一個教程,因爲我想自己學習一些科目。這是其中一次,有其他人已經編譯的資源的幫助:

感謝Sergey Podgornyy寫了這篇ES10教程
感謝 Selvaganesh寫了這篇ES10教程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章