for...of
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
for...of
語法執行一個迴圈,該迴圈操作來自可迭代物件的值序列。可迭代物件包括內置物件實例,例如 Array
、String
、TypedArray
、Map
、Set
、NodeList
(以及其他 DOM 集合),還包括 arguments
物件、由生成器函數生成的生成器,以及用戶定義的可迭代物件。
嘗試一下
語法
說明
for...of
迴圈依序逐個操作來自可迭代物件的值。迴圈對值的每次操作稱為一次迭代,而迴圈本身則稱為迭代可迭代物件,每次迭代執行可能參考當前序列值的語句。
當 for...of
迴圈在可迭代物件上進行迭代時,它首先調用可迭代物件的 [Symbol.iterator]()
方法,該方法回傳一個迭代器,然後重複調用得到的迭代器的 next()
方法,以生成要賦予 variable
的值序列。
for...of
迴圈在迭代器完成時退出(即迭代器的 next()
方法回傳一個包含 done: true
的物件)。你也可以使用流程控制語句來改變正常的控制流程。break
會退出迴圈並轉到迴圈區塊後的第一個語句,而 continue
會跳過當前迭代的其餘語句並進行下一次迭代。
如果 for...of
迴圈提前退出(例如遇到 break
語句或拋出錯誤),則會調用迭代器的 return()
方法來執行任何清理動作。
for...of
的 variable
部分可以接受任何在 =
運算符之前的東西。只要在迴圈主體內不重新賦值(它可以在迭代之間改變,因為它們是兩個獨立的變數),你可以使用 const
來宣告變數;否則,你可以使用 let
。
const iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
備註:每次迭代都會創建一個新的變數。在迴圈主體內重新賦值不會影響可迭代物件(在本例中是一個陣列)中的原始值。
你可以使用解構賦值指派多個局部變數,或者使用屬性訪問子(如 for (x.y of iterable)
)賦值給物件屬性。
然而,有一條特別規則──禁止以 async
作為變數名稱,這是無效語法:
let async;
for (async of [1, 2, 3]); // SyntaxError: The left-hand side of a for-of loop may not be 'async'.
這是為了避免和有效程式碼 for (async of => {};;)
出現語法歧異,該程式碼是一個 for
迴圈。
範例
迭代陣列
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
// 10
// 20
// 30
迭代字串
字串將依 Unicode 編碼位置迭代。
const iterable = "boo";
for (const value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
迭代 TypedArray
const iterable = new Uint8Array([0x00, 0xff]);
for (const value of iterable) {
console.log(value);
}
// 0
// 255
迭代 Map
const iterable = new Map([
["a", 1],
["b", 2],
["c", 3],
]);
for (const entry of iterable) {
console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]
for (const [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
迭代 Set
const iterable = new Set([1, 1, 2, 2, 3, 3]);
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
迭代參數物件
你可以迭代 arguments
物件來檢查傳給函數的所有參數。
function foo() {
for (const value of arguments) {
console.log(value);
}
}
foo(1, 2, 3);
// 1
// 2
// 3
迭代 NodeList
迭代用戶定義的可迭代物件
迭代帶有回傳自訂迭代器的 [Symbol.iterator]()
方法的物件:
const iterable = {
[Symbol.iterator]() {
let i = 1;
return {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
};
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
迭代帶有 [Symbol.iterator]()
生成器方法的物件:
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
可迭代迭代器(帶有回傳 this
的 [Symbol.iterator]()
方法的迭代器)是一種相當常見的技術,用來使迭代器在期望可迭代物件的語法中使用,例如 for...of
。
let i = 1;
const iterator = {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
[Symbol.iterator]() {
return this;
},
};
for (const value of iterator) {
console.log(value);
}
// 1
// 2
// 3
迭代生成器
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
}
// 1
// 2
// 3
提前退出
在第一個迴圈中執行 break
會導致迴圈提前退出。迭代器尚未完成,因此第二個迴圈將從第一個迴圈停止的地方接續執行。
const source = [1, 2, 3];
const iterator = source[Symbol.iterator]();
for (const value of iterator) {
console.log(value);
if (value === 1) {
break;
}
console.log("這個字串不會被輸出。");
}
// 1
// 另一個使用相同迭代器的迴圈會從上個迴圈的中斷處接續執行。
for (const value of iterator) {
console.log(value);
}
// 2
// 3
// 迭代器已用完。該迴圈不會執行任何迭代。
for (const value of iterator) {
console.log(value);
}
// [沒有輸出]
生成器實現了 return()
方法,當迴圈退出時,該方法會使生成器函數提前回傳,使得生成器在迴圈間不可重複使用。
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
if (value === 1) {
break;
}
console.log("這個字串不會被輸出。");
}
// 1
// 生成器已用完。該迴圈不會執行任何迭代。
for (const value of generator) {
console.log(value);
}
// [沒有輸出]
for...of
與 for...in
之間的差別
for...in
和 for...of
都用於迭代某個東西,它們之間的主要差別在於迭代的對象。
for...in
用於迭代物件的可枚舉字串屬性,而 for...of
用於迭代可迭代物件定義的要進行迭代的值。
下面的範例演示了在迭代 Array
時,for...of
迴圈和 for...in
迴圈之間的差別。
Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
const iterable = [3, 5, 7];
iterable.foo = "hello";
for (const i in iterable) {
console.log(i);
}
// "0"、"1"、"2"、"foo"、"arrCustom"、"objCustom"
for (const i in iterable) {
if (Object.hasOwn(iterable, i)) {
console.log(i);
}
}
// "0" "1" "2" "foo"
for (const i of iterable) {
console.log(i);
}
// 3 5 7
iterable
物件繼承了 objCustom
和 arrCustom
屬性,因為其原型鏈中同時包含了 Object.prototype
和 Array.prototype
。
for...in
迴圈只輸出了 iterable
物件的可枚舉屬性。它不會輸出陣列中的元素 3
、5
、7
或 "hello"
,因為它們不是屬性,而是值。它輸出了陣列的索引以及 arrCustom
和 objCustom
,它們是實際的屬性。如果你對為什麼迭代這些屬性感到困惑,可以查看關於陣列迭代和 for...in
的工作原理,裡面有更詳細的解釋。
第二個迴圈與第一個迴圈類似,但它使用 Object.hasOwn()
來檢查找到的可枚舉屬性是否為物件的自有屬性,即非繼承屬性。如果是,則輸出該屬性。屬性 0
、1
、2
和 foo
皆被輸出,因為它們是自有屬性。屬性 arrCustom
和 objCustom
都沒有被輸出,因為它們是繼承屬性。
for...of
迴圈迭代並輸出 iterable
按照可迭代陣列定義要進行迭代的值。物件的元素 3
、5
、7
被輸出,但物件的屬性沒有被輸出。
規範
Specification |
---|
ECMAScript Language Specification # sec-for-in-and-for-of-statements |
瀏覽器相容性
BCD tables only load in the browser