for...of
for...of 語句在一個可迭代物件(iterable object)上建立一個迴圈,對該物件的一系列值進行操作。可迭代物件包括內建的 Array、String、TypedArray、Map、Set、NodeList(以及其他 DOM 集合)的例項,以及 arguments 物件、生成器函式生成的生成器和使用者自定義的可迭代物件。
試一試
const array = ["a", "b", "c"];
for (const element of array) {
console.log(element);
}
// Expected output: "a"
// Expected output: "b"
// Expected output: "c"
語法
for (variable of iterable)
statement
描述
一個 for...of 迴圈按順序逐個操作來自可迭代物件的值。迴圈對一個值的每次操作稱為一次迭代(iteration),我們稱該迴圈在遍歷(iterate over)該可迭代物件。每次迭代都會執行可能引用當前序列值的語句。
當一個 for...of 迴圈遍歷一個可迭代物件時,它首先呼叫該可迭代物件的 [Symbol.iterator]() 方法,該方法返回一個迭代器(iterator),然後重複呼叫返回的迭代器的 next() 方法,以產生一系列要賦給 variable 的值。
當迭代器完成後(next() 的結果是一個 done: true 的物件),for...of 迴圈就會退出。與其他迴圈語句一樣,你可以在 statement 中使用控制流語句:
如果 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
注意:每次迭代都會建立一個新的變數。在迴圈體內重新賦值變數不會影響可迭代物件中的原始值(在本例中是一個數組)。
使用 using 或 await using 宣告的變數在每次迴圈迭代完成時都會被釋放(dispose)(並且 await using 會在迭代結束時導致一個隱式的 await)。然而,如果迴圈提前退出,迭代器中任何尚未被訪問的值都不會被釋放(儘管當前值會被釋放)。
const resources = [dbConnection1, dbConnection2, dbConnection3];
for (using dbConnection of resources) {
dbConnection.query("...");
// dbConnection is disposed here
}
你可以使用解構來賦給多個區域性變數,或者使用像 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 迴圈)產生語法歧義。
同樣,如果你使用 using 宣告,那麼變數不能被命名為 of:
for (using of of []); // SyntaxError
這是為了避免在引入 using 之前,與合法的程式碼 for (using of []) 產生語法歧義。
示例
遍歷陣列
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 物件
你可以遍歷 arguments 物件來檢查傳入函式的所有引數。
function foo() {
for (const value of arguments) {
console.log(value);
}
}
foo(1, 2, 3);
// 1
// 2
// 3
遍歷 NodeList
下面的示例透過遍歷一個 NodeList DOM 集合,為作為 <article> 元素直接後代的段落新增一個 read 類。
const articleParagraphs = document.querySelectorAll("article > p");
for (const paragraph of articleParagraphs) {
paragraph.classList.add("read");
}
遍歷使用者自定義的可迭代物件
遍歷一個帶有返回自定義迭代器的 [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("This string will not be logged.");
}
// 1
// Another loop using the same iterator
// picks up where the last loop left off.
for (const value of iterator) {
console.log(value);
}
// 2
// 3
// The iterator is used up.
// This loop will execute no iterations.
for (const value of iterator) {
console.log(value);
}
// [No output]
生成器實現了 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("This string will not be logged.");
}
// 1
// The generator is used up.
// This loop will execute no iterations.
for (const value of generator) {
console.log(value);
}
// [No output]
for...of 和 for...in 的區別
for...in 和 for...of 語句都用於遍歷某些東西。它們之間的主要區別在於遍歷的內容。
for...in 語句遍歷一個物件的可列舉字串屬性,而 for...of 語句遍歷可迭代物件定義為要迭代的值。
下面的例子展示了當 for...of 迴圈和 for...in 迴圈用於Array時的區別。
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 被顯示出來,但物件的任何屬性都沒有。
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-for-in-and-for-of-statements |
瀏覽器相容性
載入中…