17. for-of 迴圈
目錄
請支持這本書:購買它(PDF、EPUB、MOBI)捐款
(廣告,請不要封鎖。)

17. for-of 迴圈



17.1 概觀

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

17.2 介紹 for-of 迴圈

for-of 讓您能迴圈處理可迭代的資料結構:陣列、字串、Map、Set 等。可迭代性如何運作的詳細說明,請參閱章節「可迭代物件和迭代器」。不過,如果您使用 for-of 迴圈,就不需要了解這些細節。

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

// Output:
// a
// b

for-of 會逐一瀏覽 iterable 的項目,並在執行主體之前將它們指定給迴圈變數 xx 的範圍是迴圈,它只存在於迴圈內部。

您可以使用 breakcontinue

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

// Output:
// a

for-of 結合了以下優點:

17.3 陷阱:for-of 僅適用於可迭代值

of 子句的運算元必須可迭代。這表示如果您想要迴圈處理一般物件,您需要一個輔助函式(請參閱「一般物件不可迭代」)。如果一個值類似陣列,您可以透過 Array.from() 將它轉換為陣列。

// Array-like, but not iterable!
const arrayLike = { length: 2, 0: 'a', 1: 'b' };

for (const x of arrayLike) { // TypeError
    console.log(x);
}

for (const x of Array.from(arrayLike)) { // OK
    console.log(x);
}

17.4 迭代變數:const 宣告與 var 宣告

如果您對迭代變數使用 const 宣告,每個迭代都會建立一個新的 繫結(儲存空間)。這可以在以下程式碼片段中看到,我們透過箭頭函式儲存 elem 的目前繫結以供稍後使用。之後,您可以看到箭頭函式不會共用 elem 的相同繫結,它們各自有不同的繫結。

const arr = [];
for (const elem of [0, 1, 2]) {
    arr.push(() => elem); // save `elem` for later
}
console.log(arr.map(f => f())); // [0, 1, 2]

// `elem` only exists inside the loop:
console.log(elem); // ReferenceError: elem is not defined

let 宣告的工作方式與 const 宣告相同(但繫結是可變的)。

如果您對迭代變數使用 var 宣告,可以清楚地看出事情有何不同。現在所有箭頭函式都會參考 elem 的相同繫結。

const arr = [];
for (var elem of [0, 1, 2]) {
    arr.push(() => elem);
}
console.log(arr.map(f => f())); // [2, 2, 2]

// `elem` exists in the surrounding function:
console.log(elem); // 2

當您透過迴圈建立函式(例如,新增事件監聽器)時,每個迭代有一個繫結非常有幫助。

您也可以在 for 迴圈(透過 let)和 for-in 迴圈(透過 constlet)中取得每個迭代的繫結。詳細說明請參閱 變數章節

17.5 使用現有變數、物件屬性和陣列元素進行迭代

到目前為止,我們只看過使用宣告迭代變數的 for-of。但還有其他幾種形式。

您可以使用現有變數進行迭代

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

您也可以使用物件屬性進行迭代

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

您還可以使用陣列元素進行迭代

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

17.6 使用解構模式進行迭代

for-of 與解構結合使用,對於可迭代的 [key, value] 成對(編碼為陣列)特別有用。這就是 Map 的用途

const map = new Map().set(false, 'no').set(true, 'yes');
for (const [k,v] of map) {
    console.log(`key = ${k}, value = ${v}`);
}
// Output:
// key = false, value = no
// key = true, value = yes

Array.prototype.entries() 也會傳回可迭代的 [key, value] 成對

const arr = ['a', 'b', 'c'];
for (const [k,v] of arr.entries()) {
    console.log(`key = ${k}, value = ${v}`);
}
// Output:
// key = 0, value = a
// key = 1, value = b
// key = 2, value = c

因此,entries() 提供一種方式讓您可以根據迭代項目在不同位置的不同處理方式。

/** Same as arr.join(', ') */
function toString(arr) {
    let result = '';
    for (const [i,elem] of arr.entries()) {
        if (i > 0) {
            result += ', ';
        }
        result += String(elem);
    }
    return result;
}

此函式使用方式如下

> toString(['eeny', 'meeny', 'miny', 'moe'])
'eeny, meeny, miny, moe'
下一篇:18. 新陣列功能