Skip to content

ECMAScript 11 學習筆記

參考學習資源:阮一峰 ECMAScript 6 (ES6) 標準入門教程

Promise.allSettled()

Promise.allSettled() 方法回傳一個在所有給定的 promise 都已經 fulfilled 或 rejected 後的 promise,並帶有一個物件陣列,每個物件代表對應的 promise 結果。

有時候,我們希望等到一組非同步操作都結束了,不管每一個操作是成功還是失敗,再進行下一步操作。而 Promise.all() 方法只適合所有非同步操作都成功的情況,如果有一個操作失敗,就無法滿足要求。

為了解決這個問題,ES2020 引入了 Promise.allSettled() 方法,用來確定一組非同步操作是否都結束了(不管成功或失敗)。

js
Promise.allSettled([ajax('1.json'), ajax('2.json'), ajax('33.json')]).then((res) => {
  console.log(res);
  const data = res.filter((item) => item.status === 'fulfilled');
  console.log(data.flatMap((item) => item.value.data));
});

module 新增

動態匯入 import()

標準用法的 import 匯入的模組是靜態的,會使所有被匯入的模組,在載入時就被編譯(無法做到按需編譯,降低首頁載入速度)。有些場景中,你可能會希望根據條件匯入模組或者按需匯入模組,這時你可以使用動態匯入代替靜態匯入。

html
<body>
  <button id="login">login</button>
  <script type="module">
    function login() {
      return '管理員';
      // return '普通用戶';
    }

    const obtn = document.querySelector('#login');
    obtn.onclick = function () {
      let role = login();
      console.log(role);

      render(role);
    };

    async function render(role) {
      if (role === '管理員') {
        const res = await import('./1.js');
        console.log(res);
      } else {
        const res = await import('./2.js');
        console.log(res);
      }
    }
  </script>
</body>
js
// 1.js
console.log('1.js 載入了');

export default {
  name: '管理員模組',
};

export function test() {}

// 2.js
console.log('2.js 載入了');

export default {
  name: '普通用戶模組',
};

import.meta

import.meta 會回傳一個物件,有一個 url 屬性,回傳目前模組的 url 路徑,只能在模組內部使用。

js
// 在剛剛的 2.js 裡打印一下 import.meta
console.log('2.js 載入了', import.meta);

export default {
  name: '普通用戶模組',
};

export * as obj from 'module'

js
export * as obj from 'module';

// 等同於
import * as obj from 'module';
export { obj };

字串的 matchAll 方法

matchAll() 方法回傳一個包含所有對應正規表達式的結果的迭代器。可以使用 for...of 遍歷,或者使用展開運算子(...)或者 Array.from 轉換成陣列。

js
const str = `
  <ul>
    <li>11111</li>
    <li>22222</li>
    <li>33333</li>
    <li>55555</li>
  </ul>
`;

const reg = /<li>(?<content>.*)<\/li>/g;

// 使用 exec
let match = null;
const list = [];
while ((match = reg.exec(str))) {
  list.push(match.groups.content);
}
console.log(list);
// prints: ['11111', '22222', '33333', '55555']

// 使用 matchAll
const iObj = str.matchAll(reg);
console.log([...iObj]);

BigInt

JavaScript 能夠準確表示的整數範圍在 -2^53 到 2^53 之間(不含兩個端點),超過這個範圍,無法精確表示這個值,這使得 JavaScript 不適合進行科學和金融方面的精確計算。

js
9007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992

// 超過 53 個二進位的數值,無法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true

// 超過 2 的 1024 次方的數值,無法表示
Math.pow(2, 1024) // Infinity

為了與 Number 型別區分,BigInt 型別的資料必須加上一個後綴 n

js
1234 // 普通整數
1234n // BigInt

// BigInt 的運算
1n + 2n // 3n

BigInt 函式

JavaScript 原生提供 BigInt 函式,可以用它生成 BigInt 型別的數值。轉換規則基本與 Number() 一致,將其他型別的值轉為 BigInt。

js
BigInt(123) // 123n
BigInt('123') // 123n
BigInt(false) // 0n
BigInt(true) // 1n

使用 BigInt 來計算大整數:

js
console.log(BigInt(2 ** 53) + BigInt(1)); // 9007199254740993n
console.log(BigInt(2 ** 53) + BigInt(2)); // 9007199254740994n
console.log(BigInt(2 ** 53) + BigInt(3)); // 9007199254740995n

補充:json-bigint

json-bigint 是一個可以用來處理帶有大整數資料的 json 格式轉換的套件。

sh
npm i json-bigint

假設今天後端傳回來的 id 不是字串而是一個很大的數值時,可以使用這個套件來協助處理。

js
import JSONbig from 'json-bigint';

const JSONBigIntStr = JSONbig({ storeAsString: true });
const JSONBigIntNative = JSONbig({ useNativeBigInt: true });

const jsonStr = `
  {
    "id": 9007199254740993,
    "list": []
  }
`;

console.log(JSONBigIntStr.parse(jsonStr).id);    // 9007199254740993 (字串)
console.log(JSONBigIntNative.parse(jsonStr).id); // 9007199254740993n (BigInt)

globalThis

globalThis 提供了一個標準的方式來獲取不同環境下的全域 this 物件(也就是全域物件自己)。不像 window 或者 self 這些屬性,它確保可以在有無窗口的各種環境下正常工作。所以,你可以安心的使用 globalThis,不必擔心它的執行環境。為便於記憶,只需記住全域作用域中的 this 就是 globalThis

  • 瀏覽器裡面,全域(頂層)物件是 window,但 Node 和 Web Worker 沒有 window
  • 瀏覽器和 Web Worker 裡面,self 也指向全域物件,但是 Node 沒有 self
  • Node 裡面,全域物件是 global,但其他環境都不支持。
js
// 以前
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');
};

const globals = getGlobal();

if (globals.document) {
  console.log('執行 dom 操作');
} else {
  console.log('不能執行 dom 操作');
}

// 現在
if (globalThis.document) {
  console.log('執行 dom 操作');
} else {
  console.log('不能執行 dom 操作');
}

Nullish coalescing operator (??)

nullish coalescing operator 中文直翻的話叫做「空值合併運算子」,是在 ES2020 引入的一個新的 Null 判斷運算子。在 JavaScript 的表示方式為兩個問號 ??,它的行為類似於 ||,但是只有運算子左側的值為 nullundefined 時,才會回傳右側的值。

js
const obj = {
  name: 'sheep',
  introduction: 0,
};

console.log(obj.introduction || '這人很懶,什麼都沒寫'); // 這人很懶,什麼都沒寫
console.log(obj.introduction ?? '這人很懶,什麼都沒寫'); // 0
console.log(NaN || 'yo'); // yo
console.log(NaN ?? 'yo'); // NaN

??|| 的區別是什麼呢?

它們兩個最大的區別就是 ''0?? 的左側為 '' 或是 0 的時候,依然會回傳左側的值;|| 會對左側的資料進行 boolean 型別轉換,所以 ''0 會被轉換成 false,回傳右側的值。

Optional chaining (?.)

可選串連(?.),前面的值如果是 nullundefined,則不再執行後面的,直接回傳 undefined

js
const obj = {
  id: '12345',
  // profile: {
  //   name: 'sheep',
  // },
};

// ES11 以前
console.log(obj && obj.profile && obj.profile.name);
// 有了 ?. 之後
console.log(obj?.profile?.name);