盤點 ES12 中的一些新特性!

前端小智發表於2022-01-21
作者:KUMAR HARSH
譯者:前端小智
來源:blog

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

今天主要介紹一下 ECMAScript 2021(ES12)的一部分的 JS 功能。

  1. 邏輯賦值操作符
  2. 數字分隔符(1_000)
  3. Promise.any 與 AggregateError
  4. String.prototype.replaceAll
  5. WeakRefs 與 FinalizationRegistry 物件

邏輯賦值操作符

邏輯賦值操作符將邏輯操作(&&||??)與賦值表示式組合在一起。

x ||= y;
x || (x = y);

x &&= y;
x && (x = y);


x ??= y;
x ?? (x = y);

帶有&&的邏輯賦值操作符

let x = 1;
let y = 2;
x &&= y;
console.log(x); // 2

x &&= y 等價於 x && (x = y)

或者等價於

if(x) {
  x = y
}

因為x是一個真值,所以它被賦值為y,即2

帶有||的邏輯賦值操作符

let x = 1;
let y = 2;
x ||= y;
console.log(x); // 1

x ||= y 等價於 x || (x = y)

這意味著賦值操作只在x為虛值時才會發生。在我們的程式碼中,x包含1,這是一個真值,因此,賦值不會發生。這就是我們的程式碼在控制檯中列印1的原因。

簡單地說

const updateID = user => {

  // 我們可以這樣做
  if (!user.id) user.id = 1

  // 或者這樣
  user.id = user.id || 1

  // 或者這樣
  user.id ||= 1
}

帶有??的邏輯賦值操作符

?? 在 JS 中專門檢查一個值是否為 nullundefined

let a;
let b = a ?? 5;
console.log(b); // 5

在第二行,let b = a ?? 5,如果a的值為nullundefined??求值並賦值給b

現在考慮??==

let x;
let y = 2;
x ??= y;
console.log(x); // 2

x ??= y 等價於 x = x ?? (x=y)

數字分隔符

它允許我們在數字之間新增下劃線(_)字元,使數字更具可讀性。

例如

const num = 100000000

被0的數量所迷惑

分隔符解決這個問題:

const num = 100_000_000

分隔符可以用於數字的整數部分和小數部分。

const num = 1_000_000.123_456

分隔符不僅可以用在整數和浮點數中,也可以用在二進位制、十六進位制、八進位制字面量中。

分隔符也適用於BigInt數字。

const trillion = 1000_000_000_000n;
console.log(trillion.toString()); // "1000000000000"

分隔符只是為了可讀性。所以,它可以放在數字內的任何地方。

const amount = 178_00; // 00 after _ for cents.

Promise.any 與 AggregateError

Promise.any()返回第一個完成的promise的值。如果所有傳遞給Promise.any()作為引數(作為陣列)的Promise都被拒絕,則丟擲一個"AggregateError"異常。

AggregateError`是一個新的Error子類,它對單個錯誤進行分組。每個AggregateError例項都包含一個對異常陣列的引用。

考慮下面例子:

下面我們有3個 promise,它們是隨機的。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));
});

p1, p2p3中,最先的完成的的由Promise.any()執行。

(async function() {
  const result = await Promise.any([p1, p2, p3]);
  console.log(result); // 列印 "A", "B" 或者 "C"
})();

如果所有的 promise 都失敗了?在這種情況下,Promise.any()丟擲AggregateError異常。
我們需要捕獲它:

const p = new Promise((resolve, reject) => reject());

try {
  (async function() {
    const result = await Promise.any([p]);
    console.log(result);
  })();
} catch(error) {
  console.log(error.errors);

為了演示的目的,在Promise.any()中我們只能它一個 promise。而這個 promise 是失敗的。上述程式碼在控制檯中記錄了以下錯誤。

image.png

String.prototype.replaceAll 方法

String.prototype.replaceAll()允許我們用一個不同的值替換字串中的所有子串例項,而不需要使用全域性正規表示式。

目前,JavaScript字串有一個replace()方法。它可以用來用另一個字串替換一個字串。

const str = "Backbencher sits at the Back";
const newStr = str.replace("Back", "Front");
console.log(newStr); // "Frontbencher sits at the Back"

如果輸入模式是一個字串,replace()方法只替換第一次出現的內容。這就是為什麼在程式碼中,"Back"的第二次出現沒有被替換。

只有將模式作為正規表示式提供時,才能進行完全替換。

const str = "Backbencher sits at the Back";
const newStr = str.replace(/Back/g, "Front");
console.log(newStr); // "Frontbencher sits at the Front"

我們來看另一個例子

const strWithPlus = '++'
const strWithComma = strWithPlus.replace(/+/g, ', ')
// , , 

這種方法需要使用正規表示式。然而,複雜的正規表示式往往是錯誤的來源。(沒有人喜歡RegEx ?)

還有另一種方法是使用String.prototype.split()Array.prototype.join()方法

const strWithPlus = '++'
const strWithComma = strWithPlus.split('+').join(', ')
// , , 

這種方法避免使用正規表示式,但是必須將字串拆分為單獨的部分(單詞),將其轉換為一個陣列,然後將陣列元素連線為一個新字串。

string.prototype.replaceAll()解決了這些問題,併為全域性替換子串提供了簡單而方便的方式:

const strWithPlus = '++'
const strWithComma = strWithPlus.replaceAll('+', ', ')
// , ,
注意:如果使用全域性正規表示式作為查詢值,那麼replacereplaceAll的行為是一樣的。

WeakRefs 與 FinalizationRegistry 物件

WeakRef 是弱引用的意思。弱引用的主要用途是實現大型物件的快取或對映。在這種情況下,我們不希望長期保留大量的記憶體來儲存這種很少使用的快取或對映。我們可以讓記憶體很快被垃圾回收,以後如果我們再次需要它,我們可以生成一個新的快取。

JS 是會自動垃圾收集。如果一個變數不再可達,JS 垃圾收集器將自動刪除它。你可以在MDN中閱讀更多關於 JS 垃圾收集的內容。

WeaseRefs(弱引用)提供了兩個新功能:

  • 使用WeakRef類建立對物件的弱引用
  • 使用FinalizationRegistry類在垃圾收集之後執行自定義收集器

簡而言之,WeakRef允許我們建立物件的弱引用,這些物件是另一個物件的屬性值,而finalizers可以用來,除其他外,移除對被垃圾收集器 "清理"過的物件的引用。

在建立使用內建快取的記憶化(memoization)函式時,如果快取中存在傳遞給函式的引數的計算值,這種技術可能很有用(前提是物件被用作快取物件的屬性值,以及它們隨後被刪除的風險),以防止重複執行函式。

在構建內聯快取時

  • 如果沒有記憶體洩漏的風險,那麼使用 Map
  • 當使用可以隨後刪除物件的鍵時,使用 WeakMap
  • 當使用可以隨後刪除的值物件時,請將MapWeakRef結合使用

提案中最後一個例子:

function makeWeakCached(f) {
  const cache = new Map()
  return key => {
    const ref = cache.get(key)
    if (ref) {
      //     
      const cached = ref.deref()
      if (cached !== undefined) return cached;
    }

    const fresh = f(key)
    //    ( )
    cache.set(key, new WeakRef(fresh))
    return fresh
  };
}

const getImageCached = makeWeakCached(getImage);
  • WeakRef建構函式接受一個引數,該引數必須是一個物件,並返回對該物件的弱引用
  • WeakRef 例項的deref方法返回兩個值中的一個。

在內建快取的情況下,finalizer被設計為在一個值物件被垃圾收集器銷燬後完成清理過程,或者更簡單地說,刪除對這樣一個物件的弱引用。

function makeWeakCached(f) {
  const cache = new Map()
  //    -   
  const cleanup = new FinalizationRegistry(key => {
    const ref = cache.get(key)
    if (ref && !ref.deref()) cache.delete(key)
  })

  return key => {
    const ref = cache.get(key)
    if (ref) {
      const cached = ref.deref()
      if (cached !== undefined) return cached
    }

    const fresh = f(key)
    cache.set(key, new WeakRef(fresh))
    //      ( )
    cleanup.register(fresh, key)
    return fresh
  }
}

const getImageCached = makeWeakCached(getImage);

~完,我是小智,持續咳嗽中,我要去休息了,記錄點贊關注,發財致富哦。


原文:https://dev.to/cenacr007_hars...

程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

交流

文章每週持續更新,可以微信搜尋【大遷世界 】第一時間閱讀,回覆【福利】有多份前端視訊等著你,本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,歡迎Star。

相關文章