for

Baseline 已廣泛支援

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

for 語句建立了一個迴圈,它由三個可選的表示式組成,這些表示式用括號括起來並用分號分隔,後面跟著一個在迴圈中執行的語句(通常是一個塊語句)。

試一試

let str = "";

for (let i = 0; i < 9; i++) {
  str += i;
}

console.log(str);
// Expected output: "012345678"

語法

js
for (initialization; condition; afterthought)
  statement
initialization 可選

在迴圈開始前只評估一次的表示式(包括賦值表示式)或變數宣告。通常用於初始化計數器變數。此表示式可以選擇使用 varlet 關鍵字宣告新變數。用 var 宣告的變數不侷限於迴圈,即它們與 for 迴圈在同一作用域中。用 let 宣告的變數是該語句的區域性變數。

此表示式的結果被丟棄。

condition 可選

在每次迴圈迭代之前評估的表示式。如果此表示式評估為 true,則執行 statement。如果表示式評估為 false,則執行退出迴圈並轉到 for 結構後面的第一個語句。

此條件測試是可選的。如果省略,條件始終評估為 true。

afterthought 可選

在每次迴圈迭代結束時評估的表示式。這發生在下一次 condition 評估之前。通常用於更新或遞增計數器變數。

statement

只要條件評估為 true,就會執行的語句。你可以使用塊語句來執行多個語句。要在迴圈中不執行任何語句,請使用空語句;)。

描述

像其他迴圈語句一樣,你可以在 statement 中使用控制流語句

  • break 會停止 statement 的執行,並跳轉到迴圈之後的第一條語句。
  • continue 停止 statement 的執行,並重新評估 afterthought,然後是 condition

示例

使用 for 迴圈

以下 for 語句首先宣告變數 i 並將其初始化為 0。它檢查 i 是否小於 9,執行隨後的兩個語句,並在每次迴圈通過後將 i 遞增 1。

js
for (let i = 0; i < 9; i++) {
  console.log(i);
  // more statements
}

初始化塊語法

初始化塊接受表示式和變數宣告。但是,表示式不能使用未加括號的 in 運算子,因為這與 for...in 迴圈存在歧義。

js
for (let i = "start" in window ? window.start : 0; i < 9; i++) {
  console.log(i);
}
// SyntaxError: 'for-in' loop variable declaration may not have an initializer.
js
// Parenthesize the whole initializer
for (let i = ("start" in window ? window.start : 0); i < 9; i++) {
  console.log(i);
}

// Parenthesize the `in` expression
for (let i = ("start" in window) ? window.start : 0; i < 9; i++) {
  console.log(i);
}

可選的 for 表示式

for 迴圈頭部中的所有三個表示式都是可選的。例如,不強制使用 initialization 塊來初始化變數。

js
let i = 0;
for (; i < 9; i++) {
  console.log(i);
  // more statements
}

initialization 塊一樣,condition 部分也是可選的。如果你省略此表示式,則必須確保在迴圈體中中斷迴圈,以免建立無限迴圈。

js
for (let i = 0; ; i++) {
  console.log(i);
  if (i > 3) break;
  // more statements
}

你也可以省略所有三個表示式。同樣,請確保使用 break 語句來結束迴圈,並修改(增加)一個變數,以便中斷語句的條件在某個時刻為 true。

js
let i = 0;

for (;;) {
  if (i > 3) break;
  console.log(i);
  i++;
}

然而,在你不完全使用所有三個表示式位置的情況下——特別是如果你沒有用第一個表示式宣告變數而是在上層作用域中修改某些東西——請考慮使用 while 迴圈,這樣可以使意圖更清晰。

js
let i = 0;

while (i <= 3) {
  console.log(i);
  i++;
}

初始化塊中的詞法宣告

在初始化塊中宣告變數與在上層作用域中宣告變數有重要的區別,尤其是在迴圈體中建立閉包時。例如,對於以下程式碼:

js
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

它按預期記錄 012。然而,如果變數在上層作用域中定義:

js
let i = 0;
for (; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

它記錄 333。原因是每個 setTimeout 都會建立一個新的閉包,該閉包圍繞 i 變數形成閉包,但如果 i 不限定於迴圈體,則所有閉包在最終被呼叫時都將引用相同的變數——並且由於 setTimeout() 的非同步性質,它將在迴圈已經退出後發生,導致所有排隊回撥的函式體中的 i 值為 3

如果你使用 var 語句作為初始化,也會發生這種情況,因為用 var 宣告的變數只具有函式作用域,而不具有詞法作用域(即,它們不能限定於迴圈體)。

js
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
// Logs 3, 3, 3

初始化塊的作用域效果可以理解為聲明發生在迴圈體內部,但恰好在 conditionafterthought 部分中可訪問。更準確地說,let 宣告是 for 迴圈的特殊情況——如果 initializationlet 宣告,那麼每次迴圈體評估之後,會發生以下情況:

  1. 會建立一個新的詞法作用域,其中包含新的 let 宣告的變數。
  2. 上一次迭代的繫結值用於重新初始化新變數。
  3. afterthought 在新作用域中評估。

因此,在 afterthought 中重新賦值新變數不會影響上一次迭代的繫結。

initialization 之後,在 condition 首次評估之前,也會建立一個新的詞法作用域。這些細節可以透過建立閉包來觀察,閉包允許在任何特定點獲取繫結。例如,在此程式碼中,在 initialization 部分中建立的閉包不會被 afterthoughti 的重新賦值更新。

js
for (let i = 0, getI = () => i; i < 3; i++) {
  console.log(getI());
}
// Logs 0, 0, 0

這不會列印 "0, 1, 2",就像 getI 在迴圈體中宣告時會發生的那樣。這是因為 getI 不會在每次迭代中重新評估——相反,該函式被建立一次並圍繞 i 變數形成閉包,該變數引用在迴圈首次初始化時宣告的變數。隨後對 i 值的更新實際上會建立名為 i 的新變數,getI 看不到這些變數。解決此問題的一種方法是每次 i 更新時重新計算 getI

js
for (let i = 0, getI = () => i; i < 3; i++, getI = () => i) {
  console.log(getI());
}
// Logs 0, 1, 2

initialization 中的 i 變數與每次迭代中的 i 變數(包括第一次迭代)是不同的。因此,在此示例中,即使迭代中的 i 值提前遞增,getI 也會返回 0。

js
for (let i = 0, getI = () => i; i < 3; ) {
  i++;
  console.log(getI());
}
// Logs 0, 0, 0

事實上,你可以捕獲 i 變數的這個初始繫結,並稍後重新賦值,這個更新的值對迴圈體是不可見的,迴圈體看到的是 i 的下一個新繫結。

js
for (
  let i = 0, getI = () => i, incrementI = () => i++;
  getI() < 3;
  incrementI()
) {
  console.log(i);
}
// Logs 0, 0, 0

這會列印 "0, 0, 0",因為每次迴圈評估中的 i 變數實際上是一個單獨的變數,但是 getIincrementI 都讀取和寫入 i 的*初始*繫結,而不是隨後宣告的值。

不帶迴圈體的 for 迴圈

以下 for 迴圈在 afterthought 部分計算節點的偏移位置,因此它不需要使用 statement 部分,而是使用分號。

js
function showOffsetPos(id) {
  let left = 0;
  let top = 0;
  for (
    let itNode = document.getElementById(id); // initialization
    itNode; // condition
    left += itNode.offsetLeft,
      top += itNode.offsetTop,
      itNode = itNode.offsetParent // afterthought
  ); // semicolon

  console.log(
    `Offset position of "${id}" element:
left: ${left}px;
top: ${top}px;`,
  );
}

showOffsetPos("content");

// Logs:
// Offset position of "content" element:
// left: 0px;
// top: 153px;

請注意,for 語句後的分號是強制性的,因為它是一個空語句。否則,for 語句會將隨後的 console.log 行作為其 statement 部分,這將導致 log 執行多次。

使用帶有兩個迭代變數的 for 迴圈

你可以使用逗號運算子在 for 迴圈中建立同時更新的兩個計數器。多個 letvar 宣告也可以用逗號連線。

js
const arr = [1, 2, 3, 4, 5, 6];
for (let l = 0, r = arr.length - 1; l < r; l++, r--) {
  console.log(arr[l], arr[r]);
}
// 1 6
// 2 5
// 3 4

規範

規範
ECMAScript® 2026 語言規範
# sec-for-statement

瀏覽器相容性

另見