for...of

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

for...of 語句在一個可迭代物件(iterable object)上建立一個迴圈,對該物件的一系列值進行操作。可迭代物件包括內建的 ArrayStringTypedArrayMapSetNodeList(以及其他 DOM 集合)的例項,以及 arguments 物件、生成器函式生成的生成器和使用者自定義的可迭代物件。

試一試

const array = ["a", "b", "c"];

for (const element of array) {
  console.log(element);
}

// Expected output: "a"
// Expected output: "b"
// Expected output: "c"

語法

js
for (variable of iterable)
  statement
variable

在每次迭代中,從序列中接收一個值。可以是使用 constletvarusingawait using 進行的宣告,也可以是一個賦值目標(例如,一個先前宣告的變數、一個物件屬性或一個解構模式)。用 var 宣告的變數不是迴圈的區域性變數,也就是說,它們與 for...of 迴圈處於相同的作用域。

iterable

一個可迭代物件。迴圈操作的值序列的來源。

statement

每次迭代時執行的語句。可以引用 variable。你可以使用塊語句來執行多條語句。

描述

一個 for...of 迴圈按順序逐個操作來自可迭代物件的值。迴圈對一個值的每次操作稱為一次迭代(iteration),我們稱該迴圈在遍歷(iterate over)該可迭代物件。每次迭代都會執行可能引用當前序列值的語句。

當一個 for...of 迴圈遍歷一個可迭代物件時,它首先呼叫該可迭代物件的 [Symbol.iterator]() 方法,該方法返回一個迭代器(iterator),然後重複呼叫返回的迭代器的 next() 方法,以產生一系列要賦給 variable 的值。

當迭代器完成後(next() 的結果是一個 done: true 的物件),for...of 迴圈就會退出。與其他迴圈語句一樣,你可以在 statement 中使用控制流語句

  • break 會停止 statement 的執行,並跳轉到迴圈之後的第一條語句。
  • continue 會停止 statement 的執行,並進入迴圈的下一次迭代。

如果 for...of 迴圈提前退出(例如,遇到 break 語句或丟擲錯誤),迭代器的 return() 方法將被呼叫以執行任何清理工作。

for...ofvariable 部分接受任何可以出現在 = 運算子之前的內容。只要不在迴圈體內重新賦值,你可以使用 const 來宣告變數(它可以在不同迭代之間改變,因為那是兩個獨立的變數)。否則,你可以使用 let

js
const iterable = [10, 20, 30];

for (let value of iterable) {
  value += 1;
  console.log(value);
}
// 11
// 21
// 31

注意:每次迭代都會建立一個新的變數。在迴圈體內重新賦值變數不會影響可迭代物件中的原始值(在本例中是一個數組)。

使用 usingawait using 宣告的變數在每次迴圈迭代完成時都會被釋放(dispose)(並且 await using 會在迭代結束時導致一個隱式的 await)。然而,如果迴圈提前退出,迭代器中任何尚未被訪問的值都不會被釋放(儘管當前值會被釋放)。

js
const resources = [dbConnection1, dbConnection2, dbConnection3];

for (using dbConnection of resources) {
  dbConnection.query("...");
  // dbConnection is disposed here
}

你可以使用解構來賦給多個區域性變數,或者使用像 for (x.y of iterable) 這樣的屬性訪問器來將值賦給一個物件屬性。

然而,有一條特殊規則禁止使用 async 作為變數名。這是無效的語法:

js
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

js
for (using of of []); // SyntaxError

這是為了避免在引入 using 之前,與合法的程式碼 for (using of []) 產生語法歧義。

示例

遍歷陣列

js
const iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value);
}
// 10
// 20
// 30

遍歷字串

字串是按 Unicode 碼位進行迭代的。

js
const iterable = "boo";

for (const value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

遍歷型別化陣列(TypedArray)

js
const iterable = new Uint8Array([0x00, 0xff]);

for (const value of iterable) {
  console.log(value);
}
// 0
// 255

遍歷 Map

js
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

js
const iterable = new Set([1, 1, 2, 2, 3, 3]);

for (const value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

遍歷 arguments 物件

你可以遍歷 arguments 物件來檢查傳入函式的所有引數。

js
function foo() {
  for (const value of arguments) {
    console.log(value);
  }
}

foo(1, 2, 3);
// 1
// 2
// 3

遍歷 NodeList

下面的示例透過遍歷一個 NodeList DOM 集合,為作為 <article> 元素直接後代的段落新增一個 read 類。

js
const articleParagraphs = document.querySelectorAll("article > p");
for (const paragraph of articleParagraphs) {
  paragraph.classList.add("read");
}

遍歷使用者自定義的可迭代物件

遍歷一個帶有返回自定義迭代器的 [Symbol.iterator]() 方法的物件

js
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]() 生成器方法的物件

js
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

js
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

遍歷生成器

js
function* source() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = source();

for (const value of generator) {
  console.log(value);
}
// 1
// 2
// 3

提前退出

在第一個迴圈中執行 break 語句導致其提前退出。迭代器尚未完成,所以第二個迴圈將從第一個迴圈停止的地方繼續。

js
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() 方法,當迴圈退出時,這會導致生成器函式提前返回。這使得生成器在不同迴圈之間不可重用。

js
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...infor...of 語句都用於遍歷某些東西。它們之間的主要區別在於遍歷的內容。

for...in 語句遍歷一個物件的可列舉字串屬性,而 for...of 語句遍歷可迭代物件定義為要迭代的值。

下面的例子展示了當 for...of 迴圈和 for...in 迴圈用於Array時的區別。

js
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 繼承了 objCustomarrCustom 屬性,因為它的原型鏈中同時包含了 Object.prototypeArray.prototype

for...in 迴圈只記錄 iterable 物件的可列舉屬性。它不記錄陣列元素 357"hello",因為那些不是屬性——它們是。它記錄陣列的索引以及 arrCustomobjCustom,這些是實際的屬性。如果你不確定為什麼這些屬性被遍歷了,可以參考關於陣列迭代和 for...in 如何工作的更詳盡解釋。

第二個迴圈與第一個類似,但它使用 Object.hasOwn() 來檢查詢到的可列舉屬性是否是物件自身的,即非繼承的。如果是,則記錄該屬性。屬性 012foo 被記錄了,因為它們是自身屬性。屬性 arrCustomobjCustom 沒有被記錄,因為它們是繼承的。

for...of 迴圈迭代並記錄 iterable 作為一個數組(它是可迭代的)所定義要迭代的。物件的元素 357 被顯示出來,但物件的任何屬性都沒有。

規範

規範
ECMAScript® 2026 語言規範
# sec-for-in-and-for-of-statements

瀏覽器相容性

另見