for...of
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