譯原文:www.martinmck.com/posts/es202…javascript
做者:Martin McKeaveney前端
譯:黃梵高java
並不是是Google,或者是其餘權力巔峯的人。JavaScript規範由稱爲TC39的委員會進行管理和迭代。TC39由各類開發人員,學術界人士和平臺愛好者組成。webpack
TC39每一年約召開6次會議,大部分在美國,但在歐洲也舉行。他們與社區合做,接受有關JavaScript新功能的建議,並逐步處理JavaScript語言建議的四個「階段」。這四個階段以下:git
Stage 0: strawman 一種推動ECMAScript發展的自由形式,任何TC39成員,或者註冊爲TC39貢獻者的會員,均可以提交。一般經過針對TC39 ECMAScript GitHub存儲庫提升PR來完成此操做github
Stage 1: proposal 該階段產生一個正式的提案。web
圍繞提案建立了一個公共GitHub存儲庫,其中包含示例,高級API,基本原理和潛在問題。正則表達式
Stage 2: draft 草案是規範的第一個版本,與最終標準中包含的特性不會有太大差異。 草案以後,原則上只接受增量修改。算法
「草案」階段意味着須要肯定提案的全部語法和語義。這涉及使用您將在JavaScript規範自己中看到的正式規範語言描述提案功能。typescript
Stage 3: candidate 候選階段,得到具體實現和用戶的反饋。 此後,只有在實現和使用過程當中出現了重大問題纔會修改。
在此階段,責任在於社區。開發人員應使用該功能並提供反饋,這隻有經過在實際開發中使用它才能實現。
Stage 4: finished 已經準備就緒,該特性會出如今年度發佈的規範之中。
塵埃落定。該建議已在社區中的實際實施中獲得了很好的測試。本提案將包含在下一版ECMAScript標準中,並將被數以百萬計的人使用。
下面就是es2020新特性的介紹!
String.prototype.matchAll
是一個實用函數,用於獲取特定正則表達式的全部匹配項(包括捕獲組,這將在後面說明)。在ES2020以前如何解決此問題?讓咱們來看一個簡單的示例並進行迭代:
const test = "climbing, oranges, jumping, flying, carrot";
複製代碼
目標是獲取全部以ing
結尾的單詞,而且返回他們去掉ing
的動詞格式
爲了達到目的,咱們使用下面的正則:
const regex = /([a-z]*)ing/g;
複製代碼
正則表達式很難,讓咱們對其分解,瞭解每一部分的原理。
([a-z]*)
-匹配任何連續包含字母a到z的字符串。咱們將其包裝在括號中()
,以使其成爲「捕獲組」。顧名思義,捕獲組就是「捕獲」與該特定部分匹配的字符組。在咱們的示例中,咱們但願匹配全部以「 ing」結尾的單詞,可是咱們真正想要的是以前的字母,所以使用捕獲組。ing
-僅匹配以「 ing」結尾的字符串。/g
-全局搜索。搜索整個輸入字符串。不要在第一次匹配成功後就停下來。咱們但願經過正則表達式查找動詞。JavaScript中的match
函數能夠傳入正則表達式。
const test = "climbing, oranges, jumping, flying, carrot";
const regex = /([a-z]*)ing/g;
test.match(regex);
// ["climbing", "jumping", "flying"]
複製代碼
但這並非咱們想要的。它返回了完整的單詞,而不僅是動詞!發生這種狀況是由於match
不支持使用/g
標誌捕獲組。match
在不須要使用捕獲組的時候很好用,但如今彷佛不太適合。
該exec
方法在正則表達式自己上執行,而不是在相似字符串上執行match
。 exec
支持捕獲組,可是使用的API稍顯笨拙。您必須不斷exec
在正則表達式上反覆調用以獲取下一個匹配項。這要求咱們建立一個無限循環並持續調用,exec
執行到沒有匹配項爲止。
const regex = /([a-z]*)ing/g;
const matches = [];
while (true) {
const match = regex.exec(test);
if (match === null) break;
matches.push(match[1]);
}
matches
// ["climb", "jump", "fly"]
複製代碼
這種方法是能夠知足需求的,可是有點混亂不清晰。這樣作的主要緣由有兩個:
/g
標誌設置在末尾時,它才執行預期的操做。若是您將正則表達式做爲變量或參數傳遞,這可能會引發混淆。/g
標誌時,RegExp
對象是有狀態的,並存儲對其最後匹配項的引用。而這一點有可能致使bug,若是你重複且不一樣的調用它。終於,咱們到了。String.prototype.matchAll
這將使咱們的生活更加輕鬆,並提供一個簡單的解決方案來支持捕獲組,並返回一個可迭代的數組,該數組能夠擴展爲數組。讓咱們將上面的代碼重構使用matchAll。
const test = "climbing, oranges, jumping, flying, carrot";
const regex = /([a-z]*)ing/g;
const matches = [...test.matchAll(regex)];
const result = matches.map(match => match[1]);
result
// ["climb", "jump", "fly"]
複製代碼
咱們獲得一個二維數組,第一個元素中的單詞全匹配**('climbing'),第二個元素中的捕獲組('climb')**。經過迭代並保留第二個元素,終於咱們獲得想要的結果。
因爲webpack的支持,這是您可能已經熟悉的一種方式。而且在生產JavaScript應用程序中常用它來進行「代碼拆分」。代碼拆分在單個頁面應用程序中很是強大。在許多狀況下,能夠大大加快初始頁面加載時間。
動態導入語法容許咱們將import
做爲可以返回promise
的函數進行調用。這對於在代碼運行時動態加載模塊特別有用。例如,您可能想基於代碼中的某些邏輯來加載某個組件或模塊。
// JavaScript for side panel is loaded
const sidePanel = await import("components/SidePanel");
sidePanel.open()
複製代碼
還支持插值。
async function openSidePanel(type = "desktop") {
// JavaScript for desktop side panel is loaded
const sidePanel = await import(`components/${type}/SidePanel`);
sidePanel.open();
}
複製代碼
這個新功能加強了咱們應用程序的性能。咱們沒必要預先加載全部的JavaScript。動態導入使咱們可以僅加載所需數量的JS控件,性能上極大提高。
JavaScript能夠處理的最大數量爲2^53
。就是這樣9007199254740991
,或者您可使用更好記一點的Number.MAX_SAFE_INTEGER
。
當你數字超過MAX_SAFE_INTEGER
時會發生什麼?
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 - wut
console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994 - WUT
複製代碼
BigInt
是ES2020中新增的類型,用於解決此問題。要將number或者string轉換爲BigInt
,可使用BigInt構造函數,也能夠n在其末尾添加a
。所以,要修正上面的示例,在將2加到後獲得相同的值Number.MAX_SAFE_INTEGER
:
BigInt(Number.MAX_SAFE_INTEGER) + 2n; // 9007199254740993n ✅
複製代碼
您可能會驚訝於在軟件開發中擁有如此龐大的數字很是廣泛。好比時間戳和惟一標識符就能夠是這麼大的數字。
例如,Twitter使用如此大的整數做爲推文的惟一鍵。若是您嘗試將JavaScript存儲爲不帶數字的數字,則會在JavaScript應用程序中看到奇怪的錯誤BigInt。您將不得不使用第三方社區包,或者將它們存儲爲字符串。這也是JavaScript開發人員在BigInt不支持的環境中解決此問題的經常使用解決方案。
假設您正在參加考試。收到結果後,您發現您正確回答了99%的問題。在大多數生活領域中,您都會充滿對勝利的但願。不過,在這種狀況下,您依然會在得分中收到一個大紅色的叉,告訴您您失敗了。
這就是Promise.all
的工做方式。Promise.all
接受一系列承諾,並同時獲取其結果。若是它們所有成功,您的Promise.all
成功。若是一項或多項失敗,您的Promise
就會被reject
。在某些狀況下,您可能須要這種處理方式,但事實上並不老是這樣。
ES2020的Promise.allSettled
在考試方面可要比Promise.all
好得多。它將使您輕拍一下,並告訴您不要擔憂1%的reject
。
當Promise
出現時,不管它被認爲是resolve
或reject
。Promise.allSettled
容許咱們傳遞一系列的Promise
,這些Promise
將在所有結束後,Promise
的返回值是一個裝滿Promise
結果的數組。讓咱們看一個例子:
const promises = [
fetch('/api1'),
fetch('/api2'),
fetch('/api3'),
];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// "fulfilled"
// "fulfilled"
// "rejected"
複製代碼
globalThis
是一個全新的標準方法用來獲取全局 this
。以前開發者會經過以下的一些方法獲取:
因爲Js的通用性,相同的JavaScript代碼能夠在NodeJS的客戶端和服務器上運行。這提出了一系列特殊的挑戰。
一個是全局對象,能夠從任何運行的代碼段中訪問它。window
在瀏覽器中,但global
在Node
中。編寫訪問此全局對象的通用代碼依賴於一些條件邏輯,這些條件邏輯可能看起來像這樣(矇住眼睛)。
(typeof window !== "undefined"
? window
: (typeof process === 'object' &&
typeof require === 'function' &&
typeof global === 'object')
? global
: this);
複製代碼
值得慶幸的是,ES2020帶來了globalThis
全局變量。如今能夠放鬆的不去考慮window
或global
而統一前端或後端代碼。
globalThis.something = "Hello"; // Works in Browser and Node.
複製代碼
for (x in obj) ...
是在不少時候都超級有用的語法,主要是遍歷對象的key值。
for (let key in obj) {
console.log(key);
}
複製代碼
該提議與在循環中迭代元素的順序和語義有關。在提出這個以前,大多數JavaScript引擎已經應用了常識,全部主流瀏覽器都按照定義它們的順序遍歷對象的屬性。可是,有些細微差異。這些主要涉及更高級的功能,例如代理。for..in循環語義從一開始就沒有包含在JavaScript規範中,可是該提議可確保每一個人在for..in工做方式上都具備一致的參考點。
可選連接多是至關長一段時間以來JavaScript中最受期待的功能之一。在對更乾淨的JavaScript代碼的影響方面,這一得分很是高!!!
當檢查嵌套對象內部的屬性時,一般必須檢查中間對象的存在。讓咱們來看一個例子:
const test = {
name: "foo",
age: 25,
address: {
number: 44,
street: "Sesame Street",
city: {
name: "Fake City",
lat: 40,
lon: 74
}
}
}
// when we want to check for the name of the city
if (test.address.city.name) {
console.log("City name exists!");
}
// City Name exists!
複製代碼
這是一個成功的例子!可是在開發中,不能老是依靠幸福的曙光照耀咱們。有時中間的值會不存在。讓咱們看一樣的例子,可是沒有給city
定義值。
const test = {
name: "foo",
age: 25,
address: {
number: 44,
street: "Sesame Street"
}
}
if (test.address.city.name) {
console.log("City name exists!");
}
// TypeError: Cannot read property 'name' of undefined
複製代碼
咱們的代碼已經報錯了。這是由於咱們試圖訪問name
的test.address.city
,可是這是一個undefined
。當嘗試讀取undefined
上的屬性時TypeError
確定會拋出以上內容。那咱們該如何解決?在以前許多JavaScript代碼中,您將看到如下解決方案:
const test = {
name: "foo",
age: 25,
address: {
number: 44,
street: "Sesame Street"
},
}
if (test.address && test.address.city && test.address.city.name) {
console.log("City name exists!");
}
// no TypeError thrown!
複製代碼
咱們的代碼如今能夠運行了,可是咱們不得不在出bug
那兒寫不少代碼來解決問題。咱們按理來講能夠作得更好!ES2020
的可選連接運算符使您可使用新的?
語法檢查對象深處是否存在值。這是使用可選連接運算符重寫的上述示例:
const test = {
name: "foo",
age: 25,
address: {
number: 44,
street: "Sesame Street"
},
}
// much cleaner.
if (test?.address?.city?.name) {
console.log("City name exists!");
}
// no TypeError thrown!
複製代碼
看起來不錯。咱們將十分長的&&
鏈,濃縮爲更加簡潔易讀的可選鏈運算符。若是鏈中的任何值是null
或者 undefined
,則表達式僅返回undefined
。
可選的連接運算符很是強大。請看如下示例可使用它的其餘方式:
const nestedProp = obj?.['prop' + 'Name']; // computed properties
const result = obj.customMethod?.(); // functions
const arrayItem = arr?.[42]; // arrays
複製代碼
空位合併運算符是一個很是簡單的名稱,聽起來很花哨。此功能使咱們可以檢查一個值是否爲null
或undefined
,若是是,則默認爲另外一個值,僅此而已。
爲何這有用?讓咱們假設一下,你的JavaScript中有五個虛值。
咱們可能有一些要檢查數值的代碼。咱們想爲球隊中的球員分配一個小隊號碼。若是他們已經有小隊號碼,咱們將保留該號碼。不然,我給他們一個叫"unssigned"的值。
const person = {
name: "John",
age: 20,
squadNumber: 100
};
const squadNumber = person.squadNumber || "unassigned";
console.log(`${person.name}s squad number is ${squadNumber}`);
// "Johns squad number is 100"
複製代碼
此代碼能夠正常工做。可是,讓咱們從一個稍微不一樣的角度來考慮。若是咱們的person
爲0
,該怎麼辦?
const person = {
name: "Dave",
age: 30,
squadNumber: 0
};
const squadNumber = person.squadNumber || "unassigned";
console.log(`${person.name}s squad number is ${squadNumber}`);
// "Daves squad number is unassigned"
複製代碼
這是不對的。Dave
已經爲球隊效力了多年。咱們的代碼有一個錯誤。發生這種狀況是由於0
,致使咱們false
條件的||
被調用。在此示例中,對結果值的檢查存在問題。您固然能夠經過如下操做解決此問題:
const person = {
name: "Dave",
age: 30,
squadNumber: 0
};
const squadNumber = person.squadNumber >= 0 ? person.squadNumber : "unassigned";
console.log(`${person.name}s squad number is ${squadNumber}`);
// "Daves squad number is 0"
複製代碼
這是一個還能夠的解決方案,但咱們可使用空位合併運算符來解決。
運算符??
能夠更好地確保咱們的值是null
或undefined
。
const person = {
name: "Dave",
age: 30,
squadNumber: 0
};
// Nullish Coalescing Operator
// If person.squadNumber is null or undefined
// set squadNumber to unassigned
const squadNumber = person.squadNumber ?? "unassigned";
console.log(`${person.name}s squad number is ${squadNumber}`);
// "Daves squad number is 0"
複製代碼
import.meta
是一個給JavaScript模塊暴露特定上下文的元數據屬性的對象。它包含了這個模塊的信息,好比說這個模塊的URL。
若是您熟悉Node,則能夠經過__dirname
或__filename
屬性與CommonJS
一塊兒使用此功能。
const fs = require("fs");
const path = require("path");
// resolves data.bin relative to the directory of this module
const bytes = fs.readFileSync(path.resolve(__dirname, "data.bin"));
複製代碼
那瀏覽器呢?這裏import.meta將會變得有用。若是要從瀏覽器中運行的JavaScript模塊導入相對路徑,則可讓import.meta
這樣作:
// Will import cool-image relative to where this module is running.
const response = await fetch(new URL("../cool-image.jpg", import.meta.url));
複製代碼
此功能對開發第三方庫的做者那但是很是有用了,由於他們不知道他們的代碼將在什麼地方以什麼方式運行的。
總而言之,ECMAScript規範中添加的最新功能爲不斷髮展和發展的JavaScript生態系統增長了更多實用性,靈活性和強大功能。看到社區繼續以如此快的速度蓬勃發展和進步,真的使人鼓舞和激動。
您可能在想:「這些聽起來不錯,可是我如何在個人項目裏使用ES2020功能呢?
如今就可使用它!在大多數現代瀏覽器和Node的最新版本中,支持全部的功能。caniuse.com
是另外一個很棒的網站,可用於檢查跨瀏覽器和Node的ES2020
新增功能的兼容性級別。
若是須要在較舊的瀏覽器或Node版本上使用這些功能,則須要babel / typescript
。