這幾年,Ecma TC39一年一次更新 ecmascript 規範標準,截止目前,以下特性已進入 finished 狀態。現在帶大家體驗種草 ES2020 新特性。
一:Promise.allSettled
Promise.all 缺陷
都知道 Promise.all 具有併發執行異步任務的能力。但它的最大問題就是如果其中某個任務出現異常(reject),所有任務都會掛掉,Promise直接進入 reject 狀態。
想象這個場景:你的頁面有三個區域,分別對應三個獨立的接口數據,使用 Promise.all 來併發三個接口,如果其中任意一個接口服務異常,狀態是reject,這會導致頁面中該三個區域數據全都無法渲染出來,因為任何 reject 都會進入catch回調, 很明顯,這是無法接受的,如下:
<code>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: "服務異常"}})/<code>
我們需要一種機制,如果併發任務中,無論一個任務正常或者異常,都會返回對應的的狀態(fulfilled 或者 rejected)與結果(業務value 或者 拒因 reason),在 then 裡面通過 filter 來過濾出想要的業務邏輯結果,這就能最大限度的保障業務當前狀態的可訪問性,而 Promise.allSettled 就是解決這問題的。
<code>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'; }));});/<code>
我自己是一名從事了多年開發的web前端老程序員,目前辭職在做自己的web前端私人定製課程,今年我花了一個月整理了一份最適合2019年學習的web前端學習乾貨,各種框架都有整理,送給每一位前端小夥伴,想要獲取的可以關注我的頭條號並在後臺私信我:前端,即可免費獲取。
二:可選鏈(Optional chaining)
可選鏈 可讓我們在查詢具有多層級的對象時,不再需要進行冗餘的各種前置校驗。
日常開發中,我們經常會遇到這種查詢
<code>var name = user && user.info && user.info.name;/<code>
又或是這種
<code>var age = user && user.info && user.info.getAge && user.info.getAge();/<code>
這是一種醜陋但又不得不做的前置校驗,否則很容易命中 Uncaught TypeError: Cannot read property... 這種錯誤,這極有可能讓你整個應用掛掉。
用了 Optional Chaining ,上面代碼會變成
<code>var name = user?.info?.name;var age = user?.info?.getAge?.();/<code>
可選鏈中的 ? 表示如果問號左邊表達式有值, 就會繼續查詢問號後面的字段。根據上面可以看出,用可選鏈可以大量簡化類似繁瑣的前置校驗操作,而且更安全。
三:空值合併運算符(Nullish coalescing Operator)
當我們查詢某個屬性時,經常會遇到,如果沒有該屬性就會設置一個默認的值。比如下面代碼中查詢玩家等級。
<code>var level = (user.data && user.data.level) || '暫無等級';/<code>
在JS中,空字符串、0 等,當進行邏輯操作符判時,會自動轉化為 false。在上面的代碼裡,如果玩家等級本身就是 0 級, 變量 level 就會被賦值 暫無等級 字符串,這是邏輯錯誤。
<code>var level;if (typeof user.level === 'number') { level = user.level;} else if (!user.level) { level = '暫無等級';} else { level = user.level;}/<code>
來看看用空值合併運算符如何處理
<code>// {// "level": 0// }var level = `${user.level}級` ?? '暫無等級';// level -> '0級'/<code>
用空值合併運算在邏輯正確的前提下,代碼更加簡潔。
空值合併運算符 與 可選鏈 相結合,可以很輕鬆處理多級查詢並賦予默認值問題。
<code>var level = user.data?.level ?? '暫無等級';/<code>
四:dynamic-import
按需 import 提案几年前就已提出,如今終於能進入ES正式規範。這裡個人理解成“按需”更為貼切。現代前端打包資源越來越大,打包成幾M的JS資源已成常態,而往往前端應用初始化時根本不需要全量加載邏輯資源,為了首屏渲染速度更快,很多時候都是按需加載,比如懶加載圖片等。而這些按需執行邏輯資源都體現在某一個事件回調中去加載。
<code>el.onclick = () => { import(`/path/current-logic.js`) .then((module) => { module.doSomthing(); }) .catch((err) => { // load error; })}/<code>
當然,webpack目前已很好的支持了該特性。
五:globalThis
Javascript 在不同的環境獲取全局對象有不通的方式,node 中通過 global, web中通過 window, self 等,有些甚至通過 this 獲取,但通過 this 是及其危險的,this 在 js 中異常複雜,它嚴重依賴當前的執行上下文,這些無疑增加了獲取全局對象的複雜性。過去獲取全局對象,可通過一個全局函數
<code>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/<code>
而 globalThis 目的就是提供一種標準化方式訪問全局對象,有了 globalThis 後,你可以在任意上下文,任意時刻都能獲取到全局對象。
六:BigInt
Js 中 Number類型只能安全的表示-(2^53-1)至 2^53-1 範的值,即Number.MINSAFEINTEGER 至Number.MAXSAFEINTEGER,超出這個範圍的整數計算或者表示會丟失精度。
<code>var num = Number.MAX_SAFE_INTEGER; // -> 9007199254740991num = num + 1; // -> 9007199254740992// 再次加 +1 後無法正常運算num = num + 1; // -> 9007199254740992// 兩個不同的值,卻返回了true9007199254740992 === 9007199254740993 // -> true/<code>
為解決此問題,ES2020提供一種新的數據類型:BigInt。使用 BigInt 有兩種方式:
在整數字面量後面加n。
<code>var bigIntNum = 9007199254740993n;/<code>
使用 BigInt 函數。
<code>var bigIntNum = BigInt(9007199254740);var anOtherBigIntNum = BigInt('9007199254740993');/<code>
通過 BigInt, 我們可以安全的進行大數整型計算。
<code>var bigNumRet = 9007199254740993n + 9007199254740993n; // -> -> 18014398509481986nbigNumRet.toString(); // -> '18014398509481986'/<code>
注意:
BigInt 是一種新的數據原始(primitive)類型。
<code>typeof 9007199254740993n; // -> 'bigint'/<code>
儘可能避免通過調用函數 BigInt 方式來實例化超大整型。因為參數的字面量實際也是 Number 類型的一次實例化,超出安全範圍的數字,可能會引起精度丟失。
七:String.prototype.matchAll
思考下面代碼
<code>var str = '<text>JS/<text><text>正則/<text>';var reg = /(.*?)/g;console.log(str.match(reg));// -> ["<text>JS/<text>", "<text>正則/<text>"]/<code>
可以看出返回的數組裡包含了父匹配項,但未匹配到子項(group)。移除全局搜索符“g”試試。
<code>var str = '<text>JS/<text><text>正則/<text>';// 注意這裡沒有全局搜素標示符“g”var reg = /(.*?)/;console.log(str.match(reg));// 上面會打印出/*[ "<text>JS/<text>", "JS", index: 0, input: "<text>JS/<text><text>正則/<text>", groups: undefined]*//<code>
這樣可以獲取到匹配的父項,包括子項(group),但只能獲取到第一個滿足的匹配字符。能看出上面無法匹配到 <text>正則 /<text>。
如果獲取到全局所有匹配項,包括子項呢?
ES2020提供了一種簡易的方式:String.prototype.matchAll, 該方法會返回一個迭代器。
<code>var str = '<text>JS/<text><text>正則/<text>';var allMatchs = str.matchAll(/(.*?)/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]*//<code>
能看出每次迭代中可獲取所有的匹配,以及本次匹配的成功的一些其他元信息。
- https://github.com/tc39/proposals/blob/master/finished-proposals.md
- prop-tc39.now.sh/
https://juejin.im/post/5e09ca40518825499a5abff7
閱讀更多 小白沖沖沖沖 的文章
關鍵字: 美好,一直在身邊 幾年 ECMAScript