本文 AI 產出,尚未審核

JavaScript 控制流程 – 迴圈(for / while / do…while)

簡介

在程式設計中,迴圈(loop) 是最常見、也是最重要的控制流程之一。它讓我們可以把重複的工作自動化,不必手動撰寫大量相同的程式碼。對於 JavaScript 這類在瀏覽器與伺服器上都廣泛使用的語言而言,熟練掌握 forwhiledo…while 三種基本迴圈,能讓你在資料處理、DOM 操作、非同步流程控制等各種情境下寫出簡潔、可讀且效能佳的程式。

本篇文章針對 初學者到中級開發者,從概念說明、實作範例、常見陷阱到最佳實踐,完整闡述 JavaScript 迴圈的使用方式。閱讀完後,你將能夠自行挑選最適合的迴圈型別,並避免在開發中踩到常見的坑。


核心概念

1. for 迴圈

最常見的迴圈形式,適合已知執行次數或需要同時操作索引與陣列元素的情況。

// 基本 for 迴圈:印出 1 到 5
for (let i = 1; i <= 5; i++) {
  console.log(i);
}
  • 初始化 (let i = 1):只在迴圈開始時執行一次。
  • 條件 (i <= 5):每次迭代前都會檢查,若為 false 則結束迴圈。
  • 遞增 (i++):每次迭代結束後執行,通常用來改變索引值。

1.1. 迴圈與陣列

const fruits = ['🍎', '🍊', '🍇', '🍌'];

// 使用 for 直接取出陣列元素
for (let i = 0; i < fruits.length; i++) {
  console.log(`第 ${i + 1} 個水果是 ${fruits[i]}`);
}

小技巧fruits.length 放在條件式中會在每次迭代都重新計算長度,若陣列很大且長度不會變,建議先存到變數以提升效能。

const len = fruits.length;
for (let i = 0; i < len; i++) {
  console.log(fruits[i]);
}

1.2. for…of:更直觀的寫法

ES6 引入的 for…of 讓我們直接遍歷可迭代物件(如陣列、字串、Set、Map),不需要手動管理索引。

for (const fruit of fruits) {
  console.log(`水果:${fruit}`);
}

注意for…of 無法直接取得索引,如需索引請搭配 Array.entries()

for (const [index, fruit] of fruits.entries()) {
  console.log(`第 ${index} 個水果是 ${fruit}`);
}

2. while 迴圈

適合執行次數不確定,但必須在每次迭代前先檢查條件的情況。

let count = 0;
while (count < 3) {
  console.log(`第 ${count + 1} 次執行`);
  count++; // 必須手動遞增,否則會變成無限迴圈
}

2.1. 讀取使用者輸入(模擬)

let input;
do {
  input = prompt('請輸入一個正整數(輸入 0 結束):');
  const num = Number(input);
  if (num > 0) console.log(`你輸入了 ${num}`);
} while (Number(input) !== 0);

上例雖使用 do…while(稍後說明),但展示了 在條件不成立前仍必須執行一次 的情境。

3. do…while 迴圈

while 的差別在於先執行一次再判斷條件,適合「至少要跑一次」的需求。

let i = 0;
do {
  console.log(`執行第 ${i + 1} 次`);
  i++;
} while (i < 3);

3.1. 例子:簡易的密碼驗證

let attempts = 0;
let password;
do {
  password = prompt('請輸入密碼(限 3 次):');
  attempts++;
  if (password === 'letmein') {
    alert('登入成功!');
    break; // 正確即跳出迴圈
  }
  alert('密碼錯誤');
} while (attempts < 3);

if (attempts === 3 && password !== 'letmein') {
  alert('已超過嘗試次數');
}

4. 迴圈的控制關鍵字

關鍵字 說明
break 立即跳出整個迴圈,常用於找到目標後停止搜尋。
continue 跳過本次迭代的剩餘程式,直接進入下一次迴圈判斷。
label + break/continue 針對多層迴圈指定跳出/繼續的目標(較少使用,需小心維護)。
// 例:找出第一個大於 10 的偶數
const numbers = [3, 7, 12, 14, 9];
let result = null;
for (const n of numbers) {
  if (n % 2 !== 0) continue;   // 只關心偶數
  if (n > 10) {
    result = n;
    break;                     // 找到即停止
  }
}
console.log(result); // 12

常見陷阱與最佳實踐

1. 無限迴圈

最常見的錯誤是遺忘在迴圈內改變條件變數,導致程式永遠卡住。

let i = 0;
while (i < 5) {
  console.log(i);
  // i++ 被遺漏 → 無限迴圈
}

解法:在撰寫迴圈時,先在紙上寫下「初始化 → 條件 → 變化」三步,確保每一步都有對應程式碼。

2. for 迴圈的變數作用域

使用 var 宣告的迴圈變數會提升(hoist)且作用域是整個函式,容易產生意外。

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // 會印出 3,3,3
}

改用 let(塊級作用域)即可解決:

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

3. 不要在條件式中執行副作用

for (let i = 0; i < arr.push(1); i++) { /* ... */ }

arr.push(1) 會在每次判斷時改變陣列,造成預期外的結果。條件式應保持純粹(只做比較)。

4. 使用 for…in 迭代陣列時的陷阱

for…in 會遍歷所有可列舉屬性(包括原型鏈),不適合陣列。

Array.prototype.foo = 'bar';
const a = [1, 2, 3];

for (const i in a) {
  console.log(i); // 0,1,2,foo ← 不想要的
}

遍歷陣列請使用 for…offorArray.prototype.forEach

5. 最佳實踐

實踐 說明
使用 let/const 取代 var 防止變數提升與全域污染。
先寫出「退出條件」再寫遞增/遞減 可減少忘記更新變數的機會。
當迭代次數已知,優先使用 for 可直接取得索引,效能上略優。
需要「至少一次」的執行時,用 do…while 讓程式意圖更清晰。
大量資料處理時,避免在迴圈內建立新物件 減少 GC 壓力,提升效能。
使用 Array 的高階方法(mapfilterreduce 可讓程式更具宣告式、可讀性更好。

實際應用場景

1. 表格資料渲染(DOM)

const users = [
  { name: 'Alice', age: 28 },
  { name: 'Bob', age: 34 },
  { name: 'Carol', age: 22 }
];

const tbody = document.querySelector('#userTable tbody');
for (const user of users) {
  const tr = document.createElement('tr');
  tr.innerHTML = `<td>${user.name}</td><td>${user.age}</td>`;
  tbody.appendChild(tr);
}

2. 分頁載入(API)

let page = 1;
let hasMore = true;

while (hasMore) {
  const response = await fetch(`/api/products?page=${page}`);
  const { data, nextPage } = await response.json();

  renderProducts(data); // 自訂函式
  if (nextPage) {
    page = nextPage;
  } else {
    hasMore = false;
  }
}

3. 文字遊戲:猜數字

const target = Math.floor(Math.random() * 100) + 1;
let guess;
let attempts = 0;

do {
  guess = Number(prompt('猜一個 1~100 的數字:'));
  attempts++;
  if (guess > target) alert('太大了!');
  else if (guess < target) alert('太小了!');
  else alert(`恭喜!你用了 ${attempts} 次猜中`);
} while (guess !== target);

4. 大量資料的批次處理(Node.js)

const fs = require('fs');
const lines = fs.readFileSync('bigfile.txt', 'utf8').split('\n');

let batch = [];
const BATCH_SIZE = 500;

for (let i = 0; i < lines.length; i++) {
  batch.push(lines[i]);
  if (batch.length === BATCH_SIZE || i === lines.length - 1) {
    // 假設 processBatch 為非同步 I/O
    await processBatch(batch);
    batch = []; // 清空以便下一批
  }
}

總結

  • forwhiledo…while 是 JavaScript 中最基本的迴圈結構,各有適用情境:已知次數 → for、條件先判斷 → while、至少執行一次 → do…while
  • for…offor…in 是 ES6 加入的語法,前者適合遍歷可迭代物件,後者僅用於遍歷物件屬性。
  • 常見陷阱包括無限迴圈、變數提升、條件副作用等,透過使用 let/const、明確的退出條件與避免在條件式中執行副作用,可大幅降低錯誤率。
  • 在實務開發中,迴圈常被用於資料渲染、API 分頁、遊戲互動、批次處理等多種場景。適當選擇迴圈類型與最佳實踐,能讓程式碼更具可讀性、效能與可維護性。

掌握了迴圈的核心概念與實作技巧後,你將能在任何 JavaScript 專案中自信地處理重複性工作,並寫出更乾淨、可靠的程式。祝你寫程式愉快! 🎉