30. ES6 的新功能概觀
目錄
請支持這本書:購買它(PDF、EPUB、MOBI)捐款
(廣告,請不要封鎖。)

30. ES6 的新功能概觀

本章節彙整了本書所有章節的概觀部分。



30.1 ES6 功能的分類

ES6 規範的引言列出了所有新功能

[ECMAScript 6] 的一些主要強化功能包括模組、類別宣告、詞彙區塊範圍、迭代器和產生器、非同步程式設計的 Promise、解構模式和適當的尾部呼叫。ECMAScript 內建函式庫已擴充,支援額外的資料抽象,包括 Map、Set 和二進制數值陣列,以及在字串和正規表示式中額外支援 Unicode 補充字元。內建函式現在可透過子類別化進行擴充。

功能有三大類

30.2 新的數字和 Math 功能

30.2.1 新的整數文字

現在可以使用二進制和八進制表示法指定整數

> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8

30.2.2 新的 Number 屬性

全域物件 Number 獲得幾個新屬性

30.2.3 新的 Math 方法

全域物件 Math 有新的方法,用於數值、三角函數和位元運算。我們來看四個範例。

Math.sign() 傳回數字的符號

> Math.sign(-8)
-1
> Math.sign(0)
0
> Math.sign(3)
1

Math.trunc() 移除了數字的小數部分

> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3

Math.log10() 計算以 10 為底的對數

> Math.log10(100)
2

Math.hypot() 計算其引數平方和的平方根(畢氏定理)

> Math.hypot(3, 4)
5    

30.3 新的字串功能

新的字串方法

> 'hello'.startsWith('hell')
true
> 'hello'.endsWith('ello')
true
> 'hello'.includes('ell')
true
> 'doo '.repeat(3)
'doo doo doo '

ES6 有一種新的字串文字,稱為範本文字

// String interpolation via template literals (in backticks)
const first = 'Jane';
const last = 'Doe';
console.log(`Hello ${first} ${last}!`);
    // Hello Jane Doe!

// Template literals also let you create strings with multiple lines
const multiLine = `
This is
a string
with multiple
lines`;

30.4 符號

符號是 ECMAScript 6 中新的基本型別。它們是透過工廠函式建立的

const mySymbol = Symbol('mySymbol');

每次呼叫工廠函式時,都會建立一個新的唯一符號。選用的參數是一個描述性字串,用於在列印符號時顯示(它沒有其他用途)

> mySymbol
Symbol(mySymbol)

30.4.1 用例 1:唯一的屬性金鑰

符號主要用作獨特的屬性鍵,符號絕不會與任何其他屬性鍵(符號或字串)衝突。例如,你可以使用儲存在 Symbol.iterator 中的符號作為方法的鍵,讓物件可迭代(可透過 for-of 迴圈和其他語言機制使用),(有關可迭代的更多資訊,請參閱迭代章節

const iterableObject = {
    [Symbol.iterator]() { // (A)
        ···
    }
}
for (const x of iterableObject) {
    console.log(x);
}
// Output:
// hello
// world

在第 A 行中,符號用作方法的鍵。這個獨特的標記讓物件可迭代,並讓我們可以使用 for-of 迴圈。

30.4.2 用例 2:表示概念的常數

在 ECMAScript 5 中,你可能使用字串來表示顏色等概念。在 ES6 中,你可以使用符號,並確保它們始終是唯一的

const COLOR_RED    = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN  = Symbol('Green');
const COLOR_BLUE   = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');

function getComplement(color) {
    switch (color) {
        case COLOR_RED:
            return COLOR_GREEN;
        case COLOR_ORANGE:
            return COLOR_BLUE;
        case COLOR_YELLOW:
            return COLOR_VIOLET;
        case COLOR_GREEN:
            return COLOR_RED;
        case COLOR_BLUE:
            return COLOR_ORANGE;
        case COLOR_VIOLET:
            return COLOR_YELLOW;
        default:
            throw new Exception('Unknown color: '+color);
    }
}

每次呼叫 Symbol('Red') 時,都會建立一個新的符號。因此,COLOR_RED 絕不會被誤認為另一個值。如果它是字串 'Red',情況就會不同。

30.4.3 陷阱:你無法將符號強制轉換為字串

將符號強制轉換(隱式轉換)為字串會擲回例外

const sym = Symbol('desc');

const str1 = '' + sym; // TypeError
const str2 = `${sym}`; // TypeError

唯一的解決方案是明確轉換

const str2 = String(sym); // 'Symbol(desc)'
const str3 = sym.toString(); // 'Symbol(desc)'

禁止強制轉換可以防止一些錯誤,但也會讓使用符號變得更複雜。

下列操作知道符號作為屬性鍵

下列操作忽略符號作為屬性鍵

30.5 範本字串

ES6 有兩種新的字面值:範本字串標籤範本字串。這兩種字面值名稱相似,看起來也很相似,但它們有很大的不同。因此,區分它們很重要

範本字串是可跨多行且包含內插運算式(透過 ${···} 插入)的字串字面值

const firstName = 'Jane';
console.log(`Hello ${firstName}!
How are you
today?`);

// Output:
// Hello Jane!
// How are you
// today?

標記範本字串(簡稱:標記範本)是透過在範本字串之前提及函式來建立

> String.raw`A \tagged\ template`
'A \\tagged\\ template'

標記範本是函式呼叫。在先前的範例中,呼叫 String.raw 方法來產生標記範本的結果。

30.6 變數和範圍

ES6 提供兩種宣告變數的新方法:letconst,它們大部分取代了 ES5 宣告變數的方法 var

30.6.1 let

let 的運作方式類似於 var,但它宣告的變數為區塊範圍,它只存在於目前的區塊中。var函式範圍

在以下程式碼中,您可以看到 let 宣告的變數 tmp 只存在於從 A 行開始的區塊中

function order(x, y) {
    if (x > y) { // (A)
        let tmp = x;
        x = y;
        y = tmp;
    }
    console.log(tmp===x); // ReferenceError: tmp is not defined
    return [x, y];
}

30.6.2 const

const 的運作方式類似於 let,但您宣告的變數必須立即初始化,且其值無法在之後變更。

const foo;
    // SyntaxError: missing = in const declaration

const bar = 123;
bar = 456;
    // TypeError: `bar` is read-only

由於 for-of 會為每個迴圈反覆運算建立一個繫結(變數的儲存空間),因此可以 const 宣告迴圈變數

for (const x of ['a', 'b']) {
    console.log(x);
}
// Output:
// a
// b

30.6.3 宣告變數的方法

下表概述了 ES6 中宣告變數的六種方法(靈感來自 kangax 的表格

  提升 範圍 建立全域屬性
var 宣告 函式
let 暫時性死區 區塊
const 暫時性死區 區塊
函式 完整 區塊
類別 區塊
import 完整 模組全域

30.7 解構

解構是一種從儲存在(可能是巢狀)物件和陣列中的資料中提取多個值的便利方法。它可用於接收資料的位置(例如指派的左側)。如何提取值是透過模式指定的(請繼續閱讀以取得範例)。

30.7.1 物件解構

解構物件

const obj = { first: 'Jane', last: 'Doe' };
const {first: f, last: l} = obj;
    // f = 'Jane'; l = 'Doe'

// {prop} is short for {prop: prop}
const {first, last} = obj;
    // first = 'Jane'; last = 'Doe'

解構有助於處理回傳值

const obj = { foo: 123 };

const {writable, configurable} =
    Object.getOwnPropertyDescriptor(obj, 'foo');

console.log(writable, configurable); // true true

30.7.2 陣列解構

陣列解構(適用於所有可迭代值)

const iterable = ['a', 'b'];
const [x, y] = iterable;
    // x = 'a'; y = 'b'

解構有助於處理回傳值

const [all, year, month, day] =
    /^(\d\d\d\d)-(\d\d)-(\d\d)$/
    .exec('2999-12-31');

30.7.3 解構可以使用在哪裡?

解構可以在以下位置使用(我示範陣列模式;物件模式也一樣)

// Variable declarations:
const [x] = ['a'];
let [x] = ['a'];
var [x] = ['a'];

// Assignments:
[x] = ['a'];

// Parameter definitions:
function f([x]) { ··· }
f(['a']);

您也可以在 for-of 迴圈中解構

const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
    console.log(index, element);
}
// Output:
// 0 a
// 1 b

30.8 參數處理

ECMAScript 6 中的參數處理已大幅升級。現在支援參數預設值、剩餘參數(變數參數)和解構。

此外,展開運算子有助於函式/方法/建構式呼叫和陣列文字。

30.8.1 預設參數值

預設參數值是透過等號 (=) 為參數指定的。如果呼叫者未提供參數值,則會使用預設值。在以下範例中,y 的預設參數值為 0

function func(x, y=0) {
    return [x, y];
}
func(1, 2); // [1, 2]
func(1); // [1, 0]
func(); // [undefined, 0]

30.8.2 剩餘參數

如果您在參數名稱前加上剩餘運算子 (...),該參數會透過陣列接收所有剩餘參數

function format(pattern, ...params) {
    return {pattern, params};
}
format(1, 2, 3);
    // { pattern: 1, params: [ 2, 3 ] }
format();
    // { pattern: undefined, params: [] }

30.8.3 透過解構的命名參數

如果您在參數清單中使用物件模式進行解構,您可以模擬命名參數

function selectEntries({ start=0, end=-1, step=1 } = {}) { // (A)
    // The object pattern is an abbreviation of:
    // { start: start=0, end: end=-1, step: step=1 }

    // Use the variables `start`, `end` and `step` here
    ···
}

selectEntries({ start: 10, end: 30, step: 2 });
selectEntries({ step: 3 });
selectEntries({});
selectEntries();

A 行中的 = {} 讓您可以不用參數呼叫 selectEntries()

30.8.4 展開運算子 (...)

在函式和建構式呼叫中,展開運算子會將可迭代值轉換為引數

> Math.max(-1, 5, 11, 3)
11
> Math.max(...[-1, 5, 11, 3])
11
> Math.max(-1, ...[-5, 11], 3)
11

在陣列文字中,展開運算子會將可迭代值轉換為陣列元素

> [1, ...[2,3], 4]
[1, 2, 3, 4]

30.9 ECMAScript 6 中的可呼叫實體

在 ES5 中,單一建構(傳統)函式扮演三個角色

在 ES6 中,有更多專業化。現在由下列方式處理這三個職責。就函式定義和類別定義而言,定義不是宣告就是表達式。

特別是對於回呼函式,箭頭函式很方便,因為它們不會遮蔽周圍範圍的 this

對於較長的回呼函式和獨立函式,傳統函式可以。有些 API 使用 this 作為隱含參數。在這種情況下,你別無選擇,只能使用傳統函式。

請注意,我區分

即使它們的行為不同(如下所述),所有這些實體都是函式。例如

> typeof (() => {}) // arrow function
'function'
> typeof function* () {} // generator function
'function'
> typeof class {} // class
'function'

30.10 箭頭函式

箭頭函式有兩個好處。

首先,它們比傳統函式表達式簡潔

const arr = [1, 2, 3];
const squares = arr.map(x => x * x);

// Traditional function expression:
const squares = arr.map(function (x) { return x * x });

其次,它們的 this 從周圍環境(詞彙)中擷取。因此,你不再需要 bind()that = this

function UiComponent() {
    const button = document.getElementById('myButton');
    button.addEventListener('click', () => {
        console.log('CLICK');
        this.handleClick(); // lexical `this`
    });
}

以下變數在箭頭函式內部都是詞彙的

30.11 除了類別之外的新 OOP 功能

30.11.1 新的物件文字功能

方法定義

const obj = {
    myMethod(x, y) {
        ···
    }
};

屬性值簡寫

const first = 'Jane';
const last = 'Doe';

const obj = { first, last };
// Same as:
const obj = { first: first, last: last };

運算屬性金鑰

const propKey = 'foo';
const obj = {
    [propKey]: true,
    ['b'+'ar']: 123
};

這個新語法也可以用於方法定義

const obj = {
    ['h'+'ello']() {
        return 'hi';
    }
};
console.log(obj.hello()); // hi

運算屬性金鑰的主要用例是讓它容易使用符號作為屬性金鑰。

30.11.2 Object 中的新方法

Object 最重要的新方法是 assign()。傳統上,這個功能在 JavaScript 世界中稱為 extend()。與這個經典操作的運作方式相反,Object.assign() 僅考慮自己的(非繼承的)屬性。

const obj = { foo: 123 };
Object.assign(obj, { bar: true });
console.log(JSON.stringify(obj));
    // {"foo":123,"bar":true}

30.12 類別

類別和子類別

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return `(${this.x}, ${this.y})`;
    }
}

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y);
        this.color = color;
    }
    toString() {
        return super.toString() + ' in ' + this.color;
    }
}

使用類別

> const cp = new ColorPoint(25, 8, 'green');

> cp.toString();
'(25, 8) in green'

> cp instanceof ColorPoint
true
> cp instanceof Point
true

在底層,ES6 類別並不是什麼新東西:它們主要提供更方便的語法來建立舊式的建構函式。如果你使用 typeof,你可以看到

> typeof Point
'function'

30.13 模組

JavaScript 長久以來都有模組,但它們是透過函式庫實作,而非內建在語言中。ES6 是 JavaScript 首次內建模組。

ES6 模組儲存在檔案中。每個檔案只有一個模組,每個模組只有一個檔案。你可以用兩種方式從模組中匯出內容。這兩種方式可以混合使用,但通常建議分開使用。

30.13.1 多個命名匯出

可以有多個命名匯出

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

你也可以匯入整個模組

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

30.13.2 單一預設匯出

可以有一個預設匯出。例如,一個函式

//------ myFunc.js ------
export default function () { ··· } // no semicolon!

//------ main1.js ------
import myFunc from 'myFunc';
myFunc();

或一個類別

//------ MyClass.js ------
export default class { ··· } // no semicolon!

//------ main2.js ------
import MyClass from 'MyClass';
const inst = new MyClass();

請注意,如果你預設匯出函式或類別(它們是匿名宣告),結尾處不會有分號。

30.13.3 瀏覽器:指令碼與模組

  指令碼 模組
HTML 元素 <script> <script type="module">
預設模式 非嚴格 嚴格
頂層變數是 全域 模組的區域
頂層的 this window undefined
執行 同步 非同步
宣告式匯入(import 陳述式)
程式化匯入(基於 Promise 的 API)
檔案副檔名 .js .js

30.14 for-of 迴圈

for-of 是 ES6 中取代 for-inforEach() 的新迴圈,並支援新的迭代協定。

使用它來迴圈可迭代物件(陣列、字串、Map、Set 等;請參閱章節「可迭代物件和迭代器」)

const iterable = ['a', 'b'];
for (const x of iterable) {
    console.log(x);
}

// Output:
// a
// b

breakcontinue 可在 for-of 迴圈中使用

for (const x of ['a', '', 'b']) {
    if (x.length === 0) break;
    console.log(x);
}

// Output:
// a

在迴圈陣列時同時存取元素及其索引(of 前面的方括號表示我們正在使用 解構

const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
    console.log(`${index}. ${element}`);
}

// Output:
// 0. a
// 1. b

迴圈 Map 中的 [key, value] 項目(of 前面的方括號表示我們正在使用 解構

const map = new Map([
    [false, 'no'],
    [true, 'yes'],
]);
for (const [key, value] of map) {
    console.log(`${key} => ${value}`);
}

// Output:
// false => no
// true => yes

30.15 新的陣列功能

新的靜態 Array 方法

新的 Array.prototype 方法

30.16 Map 和 Set

在 ECMAScript 6 中,以下四種資料結構是新的:MapWeakMapSetWeakSet

30.16.1 Map

Map 的鍵值可以是任意值

> const map = new Map(); // create an empty Map
> const KEY = {};

> map.set(KEY, 123);
> map.get(KEY)
123
> map.has(KEY)
true
> map.delete(KEY);
true
> map.has(KEY)
false

您可以使用包含 [鍵值, 值] 成對的陣列 (或任何可迭代物件) 來設定 Map 中的初始資料

const map = new Map([
    [ 1, 'one' ],
    [ 2, 'two' ],
    [ 3, 'three' ], // trailing comma is ignored
]);

30.16.2 Set

Set 是唯一元素的集合

const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]

如您所見,如果您將可迭代物件 (範例中的 arr) 傳遞給建構函式,則可以使用元素初始化 Set。

30.16.3 WeakMap

WeakMap 是一種 Map,不會阻止其鍵值被垃圾回收。這表示您可以將資料與物件關聯,而不用擔心記憶體外洩。例如

//----- Manage listeners

const _objToListeners = new WeakMap();

function addListener(obj, listener) {
    if (! _objToListeners.has(obj)) {
        _objToListeners.set(obj, new Set());
    }
    _objToListeners.get(obj).add(listener);
}

function triggerListeners(obj) {
    const listeners = _objToListeners.get(obj);
    if (listeners) {
        for (const listener of listeners) {
            listener();
        }
    }
}

//----- Example: attach listeners to an object

const obj = {};
addListener(obj, () => console.log('hello'));
addListener(obj, () => console.log('world'));

//----- Example: trigger listeners

triggerListeners(obj);

// Output:
// hello
// world

30.17 類型化陣列

類型化陣列是 ECMAScript 6 API,用於處理二進位資料。

程式碼範例

const typedArray = new Uint8Array([0,1,2]);
console.log(typedArray.length); // 3
typedArray[0] = 5;
const normalArray = [...typedArray]; // [5,1,2]

// The elements are stored in typedArray.buffer.
// Get a different view on the same data:
const dataView = new DataView(typedArray.buffer);
console.log(dataView.getUint8(0)); // 5

ArrayBuffer 的執行個體儲存要處理的二進位資料。使用兩種「檢視」來存取資料

下列瀏覽器 API 支援類型化陣列 (詳細資訊會在專門的章節中說明)

30.18 可迭代對象和迭代器

ES6 引入了新的機制來遍歷資料:迭代。迭代有兩個核心概念

以 TypeScript 表示法表示為介面,這些角色如下所示

interface Iterable {
    [Symbol.iterator]() : Iterator;
}
interface Iterator {
    next() : IteratorResult;
}
interface IteratorResult {
    value: any;
    done: boolean;
}

30.18.1 可迭代值

下列值是可迭代的

一般物件不可迭代(原因說明於 專門章節)。

30.18.2 支援迭代的建構

透過迭代存取資料的語言建構

30.19 產生器

30.19.1 什麼是產生器?

您可以將產生器視為可以暫停和繼續的程序(程式碼片段)

function* genFunc() {
    // (A)
    console.log('First');
    yield;
    console.log('Second');
}

請注意新的語法:function*產生器函式的新「關鍵字」(還有產生器方法)。yield 是產生器可以用來暫停自己的運算子。此外,產生器還可以透過 yield 接收輸入和傳送輸出。

當您呼叫產生器函式 genFunc() 時,您會取得一個產生器物件 genObj,您可以用它來控制程序

const genObj = genFunc();

此處理程序最初在 A 行暫停。genObj.next() 繼續執行,genFunc() 內部的 yield 暫停執行

genObj.next();
// Output: First
genObj.next();
// output: Second

30.19.2 產生器的種類

有四種產生器

  1. 產生器函式宣告
     function* genFunc() { ··· }
     const genObj = genFunc();
    
  2. 產生器函式表達式
     const genFunc = function* () { ··· };
     const genObj = genFunc();
    
  3. 物件文字中的產生器方法定義
     const obj = {
         * generatorMethod() {
             ···
         }
     };
     const genObj = obj.generatorMethod();
    
  4. 類別定義中的產生器方法定義(類別宣告或類別表達式)
     class MyClass {
         * generatorMethod() {
             ···
         }
     }
     const myInst = new MyClass();
     const genObj = myInst.generatorMethod();
    

30.19.3 使用案例:實作可迭代物件

產生器傳回的物件可迭代;每個 yield 都會貢獻給迭代值序列。因此,您可以使用產生器來實作可迭代物件,而各種 ES6 語言機制可以使用這些物件:for-of 迴圈、展開運算子 (...) 等。

下列函式傳回物件屬性的可迭代物件,每個屬性會傳回一個 [key, value] 成對值

function* objectEntries(obj) {
    const propKeys = Reflect.ownKeys(obj);

    for (const propKey of propKeys) {
        // `yield` returns a value and then pauses
        // the generator. Later, execution continues
        // where it was previously paused.
        yield [propKey, obj[propKey]];
    }
}

objectEntries() 的使用方式如下

const jane = { first: 'Jane', last: 'Doe' };
for (const [key,value] of objectEntries(jane)) {
    console.log(`${key}: ${value}`);
}
// Output:
// first: Jane
// last: Doe

objectEntries() 的運作方式詳細說明於 專屬區段。在沒有產生器的情況下實作相同的功能會困難許多。

30.19.4 使用案例:更簡潔的非同步程式碼

您可以使用產生器大幅簡化 Promise 的使用。我們來看一個基於 Promise 的函式 fetchJson(),以及如何透過產生器來改善它。

function fetchJson(url) {
    return fetch(url)
    .then(request => request.text())
    .then(text => {
        return JSON.parse(text);
    })
    .catch(error => {
        console.log(`ERROR: ${error.stack}`);
    });
}

使用 函式庫 co 和產生器,這個非同步程式碼看起來像同步程式碼

const fetchJson = co.wrap(function* (url) {
    try {
        let request = yield fetch(url);
        let text = yield request.text();
        return JSON.parse(text);
    }
    catch (error) {
        console.log(`ERROR: ${error.stack}`);
    }
});

ECMAScript 2017 將有非同步函式,其內部基於產生器。使用它們,程式碼看起來像這樣

async function fetchJson(url) {
    try {
        let request = await fetch(url);
        let text = await request.text();
        return JSON.parse(text);
    }
    catch (error) {
        console.log(`ERROR: ${error.stack}`);
    }
}

所有版本都可以像這樣呼叫

fetchJson('http://example.com/some_file.json')
.then(obj => console.log(obj));

30.19.5 使用案例:接收非同步資料

產生器可以透過 yieldnext() 接收輸入。這表示當新資料非同步抵達時,您可以喚醒產生器,而產生器會覺得它同步接收資料。

30.20 新的正規表示式功能

下列正規表示式功能是 ECMAScript 6 的新功能

30.21 非同步程式設計的 Promise

Promise 是提供非同步運算結果的替代方案,相較於回呼函式。它需要非同步函式實作者的更多努力,但為這些函式的使用者提供多項好處。

下列函式透過 Promise 非同步傳回結果

function asyncFunc() {
    return new Promise(
        function (resolve, reject) {
            ···
            resolve(result);
            ···
            reject(error);
        });
}

您呼叫 asyncFunc() 的方式如下

asyncFunc()
.then(result => { ··· })
.catch(error => { ··· });

30.21.1 串連 then() 呼叫

then() 永遠傳回一個 Promise,讓您可以串連方法呼叫

asyncFunc1()
.then(result1 => {
    // Use result1
    return asyncFunction2(); // (A)
})
.then(result2 => { // (B)
    // Use result2
})
.catch(error => {
    // Handle errors of asyncFunc1() and asyncFunc2()
});

then() 傳回的 Promise P 如何解決,取決於其回呼函式執行什麼動作

此外,請注意 catch() 如何處理兩個非同步函式呼叫 (asyncFunction1()asyncFunction2()) 的錯誤。也就是說,未捕捉的錯誤會傳遞下去,直到出現錯誤處理常式。

30.21.2 平行執行非同步函式

如果您透過 then() 串連非同步函式呼叫,則它們會依序執行,一次一個

asyncFunc1()
.then(() => asyncFunc2());

如果您不這麼做,而是立即呼叫所有函式,則它們基本上會平行執行 (在 Unix 程序術語中稱為「fork」)

asyncFunc1();
asyncFunc2();

Promise.all() 讓您可以在所有結果都出來時收到通知 (在 Unix 程序術語中稱為「join」)。它的輸入是一個 Promise 陣列,其輸出是一個單一 Promise,並以結果陣列來完成。

Promise.all([
    asyncFunc1(),
    asyncFunc2(),
])
.then(([result1, result2]) => {
    ···
})
.catch(err => {
    // Receives first rejection among the Promises
    ···
});

30.21.3 詞彙表:Promise

Promise API 旨在非同步傳遞結果。Promise 物件(簡稱:Promise)是結果的替代品,透過該物件傳遞結果。

狀態

對狀態變更做出反應

變更狀態:有兩個操作可用於變更 Promise 的狀態。在您執行其中一個操作一次後,後續執行將不產生任何效果。

30.22 使用代理進行元程式設計

代理讓您可以攔截並自訂對物件執行的操作(例如取得屬性)。它們是一種元程式設計功能。

在以下範例中,proxy 是我們攔截其運算的物件,而 handler 是處理攔截的物件。在這個案例中,我們只攔截單一運算,get(取得屬性)。

const target = {};
const handler = {
    get(target, propKey, receiver) {
        console.log('get ' + propKey);
        return 123;
    }
};
const proxy = new Proxy(target, handler);

當我們取得屬性 proxy.foo 時,處理常式攔截該運算

> proxy.foo
get foo
123

參閱 完整 API 參考,以取得可攔截運算的清單。

下一頁:附註