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 number
⇨ 9007199254740991n
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.match
,match
只返回字串引數第一個符合匹配的。
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()
- 使用捕獲組時更加優雅。捕獲組知識帶有提取模式()的正規表示式的一部分。
- 它返回一個迭代器而不是陣列,迭代器本身很有用。
- 可以使用擴充套件運算子...將迭代器轉為陣列。
- 它避免使用帶
/g
標誌的正規表示式...當從資料庫或外部源檢索未知的正規表示式並與古老的RegEx物件一起使用時非常有用。 - 使用
RegExp
物件建立的正規表示式不能使用點(.
)運算子連結。 - **高階:
RegEx
**物件跟蹤最後匹配位置的內部.lastIndex
屬性,這可能對複雜案例有破壞性的事情。
.matchAll()
如何工作
這是一簡單個例子。
我們嘗試匹配字串Hello
的所有e
和l
。因為返回了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 + D800
到U + DFFF
的處理,有時可以進入JSON
字串。這可能是一個問題,因為JSON.stringify
可能會返回格式化為沒有等效UTF-8
字元的值的這些數字。但JSON
格式需要UTF-8
編碼。
JSON
物件可用於解析JSON
格式(但也更多。)JavaScript JSON
物件也具有stringify
和parse
方法。
該解析方法適用於一個結構良好的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 + 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
和類方法裡被用來定義variables
, functions
,getters
和setters
這是一個相當無意義的示例,僅關注新語法:
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是一套尚未有機會在生產環境中進行全面探索的新功能。如果您有任何更正,建議或任何其他反饋,請告訴我們。
我經常寫一個教程,因為我想自己學習一些科目。這是其中一次,有其他人已經編譯的資源的幫助: