種草 ES2020 新特性,真的學不動了

做者:李大雷
https://juejin.im/post/5e09ca40518825499a5abff7


這幾年,Ecma TC39 一年一次更新 ECMAScript 規範標準,截止目前,如下特性已進入 finished 狀態。如今帶你們體驗種草 ES2020 新特性。
前端

一:Promise.allSettled

Promise.all 缺陷

都知道 Promise.all 具備併發執行異步任務的能力。但它的最大問題就是若是其中某個任務出現異常(reject),全部任務都會掛掉,Promise 直接進入 reject 狀態。webpack

想象這個場景:你的頁面有三個區域,分別對應三個獨立的接口數據,使用 Promise.all 來併發三個接口,若是其中任意一個接口服務異常,狀態是 reject,這會致使頁面中該三個區域數據全都沒法渲染出來,由於任何 reject 都會進入 catch 回調, 很明顯,這是沒法接受的,以下:git

Promise.all([
Promise.reject({code: 500, msg: '服務異常'}),
Promise.resolve({ code: 200, list: []}),
Promise.resolve({code: 200, list: []})
])
.then((ret) => {
// 若是其中一個任務是 reject,則不會執行到這個回調。
RenderContent(ret);
})
.catch((error) => {
// 本例中會執行到這個回調
// error: {code: 500, msg: "服務異常"}
})

Promise.allSettled 的優點

咱們須要一種機制,若是併發任務中,不管一個任務正常或者異常,都會返回對應的的狀態(fulfilled 或者 rejected)與結果(業務 value 或者 拒因 reason),在 then 裏面經過 filter 來過濾出想要的業務邏輯結果,這就能最大限度的保障業務當前狀態的可訪問性,而 Promise.allSettled 就是解決這問題的。github

Promise.allSettled([
Promise.reject({code: 500, msg: '服務異常'}),
Promise.resolve({ code: 200, list: []}),
Promise.resolve({code: 200, list: []})
])
.then((ret) => {
/*
0: {status: "rejected", reason: {...}}
1: {status: "fulfilled", value: {...}}
2: {status: "fulfilled", value: {...}}
*/

// 過濾掉 rejected 狀態,儘量多的保證頁面區域數據渲染
RenderContent(ret.filter((el) => {
return el.status !== 'rejected';
}));
});

二:可選鏈

可選鏈 可以讓咱們在查詢具備多層級的對象時,再也不須要進行冗餘的各類前置校驗。web

平常開發中,咱們常常會遇到這種查詢express

var name = user && user.info && user.info.name;

又或是這種數組

var age = user && user.info && user.info.getAge && user.info.getAge();

這是一種醜陋但又不得不作的前置校驗,不然很容易命中 Uncaught TypeError: Cannot read property... 這種錯誤,這極有可能讓你整個應用掛掉。安全

用了 Optional Chaining ,上面代碼會變成微信

var name = user?.info?.name;
var age = user?.info?.getAge?.();

可選鏈中的 ? 表示若是問號左邊表達式有值, 就會繼續查詢問號後面的字段。根據上面能夠看出,用可選鏈能夠大量簡化相似繁瑣的前置校驗操做,並且更安全。併發

三:空值合併運算符

當咱們查詢某個屬性時,常常會遇到,若是沒有該屬性就會設置一個默認的值。好比下面代碼中查詢玩家等級。

var level = (user.data && user.data.level) || '暫無等級';

在 JS 中,空字符串、0 等,當進行邏輯操做符判斷時,會自動轉化爲 false。在上面的代碼裏,若是玩家等級自己就是 0 級, 變量 level 就會被賦值 暫無等級 字符串,這是邏輯錯誤。

var level;
if (typeof user.level === 'number') {
level = user.level;
} else if (!user.level) {
level = '暫無等級';
} else {
level = user.level;
}

來看看用空值合併運算符如何處理

// {
// "level": 0
// }
var level = `${user.level}級` ?? '暫無等級';
// level -> '0級'

用空值合併運算在邏輯正確的前提下,代碼更加簡潔。

空值合併運算符 與 可選鏈 相結合,能夠很輕鬆處理多級查詢並賦予默認值問題。

var level = user.data?.level ?? '暫無等級';

四:dynamic-import

按需 import 提案几年前就已提出,現在終於能進入 ES 正式規範。這裏我的理解成 "按需" 更爲貼切。現代前端打包資源愈來愈大,打包成幾 M 的 JS 資源已成常態,而每每前端應用初始化時根本不須要全量加載邏輯資源,爲了首屏渲染速度更快,不少時候都是按需加載,好比懶加載圖片等。而這些按需執行邏輯資源都體如今某一個事件回調中去加載。

el.onclick = () => {
import(`/path/current-logic.js`)
.then((module) => {
module.doSomthing();
})
.catch((err) => {
// load error;
})
}

固然,webpack 目前已很好的支持了該特性。

五:globalThis

JavaScript 在不一樣的環境獲取全局對象有不一樣的方式,NodeJS 中經過 global, Web 中經過 windowself 等,有些甚至經過 this 獲取,但經過 this 是及其危險的,this 在 JavaScript 中異常複雜,它嚴重依賴當前的執行上下文,這些無疑增長了獲取全局對象的複雜性。

過去獲取全局對象,可經過一個全局函數:

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');
};

var globals = getGlobal();

// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/globalThis

而 globalThis 目的就是提供一種標準化方式訪問全局對象,有了 globalThis後,你能夠在任意上下文,任意時刻都能獲取到全局對象。

六:BigInt

JavaScript 中 Number 類型只能安全的表示-(2^53-1)至 2^53-1 範的值,即 Number.MIN_SAFE_INTEGER 至 Number.MAX_SAFE_INTEGER,超出這個範圍的整數計算或者表示會丟失精度。

var num = Number.MAX_SAFE_INTEGER;  // -> 9007199254740991

num = num + 1; // -> 9007199254740992

// 再次加 +1 後沒法正常運算
num = num + 1; // -> 9007199254740992

// 兩個不一樣的值,卻返回了true
9007199254740992 === 9007199254740993 // -> true

爲解決此問題,ES2020 提供一種新的數據類型:BigInt。使用 BigInt 有兩種方式:

  1. 在整數字面量後面加 n
var bigIntNum = 9007199254740993n;
  1. 使用  BigInt 函數。
var bigIntNum = BigInt(9007199254740);
var anOtherBigIntNum = BigInt('9007199254740993');

經過 BigInt, 咱們能夠安全的進行大數整型計算。

var bigNumRet = 9007199254740993n + 9007199254740993n; // -> -> 18014398509481986n

bigNumRet.toString(); // -> '18014398509481986'

注意:

  1. BigInt 是一種新的數據原始(primitive)類型。
typeof 9007199254740993n; // -> 'bigint'
  1. 儘量避免經過調用函數  BigInt 方式來實例化超大整型。由於參數的字面量實際也是  Number 類型的一次實例化,超出安全範圍的數字,可能會引發精度丟失。

七:String.prototype.matchAll

The matchAll() method returns an iterator of all results matching a string against a regular expression, including capturing groups. ——MDN

思考下面代碼:

var str = '<text>JS</text><text>正則</text>';
var reg = /<\w+>(.*?)<\/\w+>/g;

console.log(str.match(reg));
// -> ["<text>JS</text>", "<text>正則</text>"]

能夠看出返回的數組裏包含了父匹配項,但未匹配到子項(group)。移除全局搜索符"g"試試。

var str = '<text>JS</text><text>正則</text>';
// 注意這裏沒有全局搜素標示符"g"
var reg = /<\w+>(.*?)<\/\w+>/;
console.log(str.match(reg));

// 上面會打印出
/*
[
"<text>JS</text>",
"JS",
index: 0,
input:
"<text>JS</text><text>正則</text>",
groups: undefined
]
*/

這樣能夠獲取到匹配的父項,包括子項(group),但只能獲取到第一個知足的匹配字符。能看出上面沒法匹配到<text>正則</text>

若是獲取到全局全部匹配項,包括子項呢?

ES2020 提供了一種簡易的方式:String.prototype.matchAll, 該方法會返回一個迭代器。

var str = '<text>JS</text><text>正則</text>';
var allMatchs = str.matchAll(/<\w+>(.*?)<\/\w+>/g);

for (const match of allMatchs) {
console.log(match);
}

/*
第一次迭代返回:
[
"<text>JS</text>",
"JS",
index: 0,
input: "<text>JS</text><text>正則</text>",
groups: undefined
]

第二次迭代返回:
[
"<text>正則</text>",
"正則",
index: 15,
input: "<text>JS</text><text>正則</text>",
groups: undefined
]
*/

能看出每次迭代中可獲取全部的匹配,以及本次匹配的成功的一些其餘元信息。

參考資料

  1. finished-proposals [1]
  2. TC39 Proposals [2]


❤️ 看完三件事

若是你以爲這篇內容對你挺有啓發,我想邀請你幫我三個小忙:

  1. 點個「在看」,讓更多的人也能看到這篇內容(喜歡不點在看,都是耍流氓 -_-)

  2. 關注個人博客 https://github.com/SHERlocked93/blog,讓咱們成爲長期關係

  3. 關注公衆號「前端下午茶」,持續爲你推送精選好文,也能夠加我爲好友,隨時聊騷。

點這,與你們一塊兒分享本文吧~


本文分享自微信公衆號 - 前端下午茶(qianduanxiawucha)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索