JavaScript 基礎:迴圈的完整指南

2026-01-26 07:53 | By justin | JavaScript
(Updated: 2026-01-26 07:53)

JavaScript 基礎:迴圈的完整指南

各位好!

這篇是 JavaScript 系列的第七單元,我們要學習迴圈(Loop)。迴圈讓我們可以重複執行程式碼,處理大量資料時特別有用。


為什麼需要迴圈?

假設你要印出 1 到 100 的數字:

// 沒有迴圈的話...
console.log(1);
console.log(2);
console.log(3);
// ... 要寫 100 行

// 使用迴圈
for (let i = 1; i <= 100; i++) {
  console.log(i);
}

for 迴圈

最常用的迴圈,適合已知重複次數的情況。

基本語法

// 語法:for (初始化; 條件; 遞增) { 執行內容 }
for (let i = 0; i < 5; i++) {
  console.log(i);
}
// 輸出:0, 1, 2, 3, 4

// 執行流程:
// 1. 初始化 i = 0
// 2. 檢查條件 i < 5(true),執行內容
// 3. 遞增 i++(i 變成 1)
// 4. 檢查條件 i < 5(true),執行內容
// 5. 重複直到條件為 false

常見用法

// 倒數
for (let i = 5; i > 0; i--) {
  console.log(i);
}
// 輸出:5, 4, 3, 2, 1

// 每次增加 2
for (let i = 0; i < 10; i += 2) {
  console.log(i);
}
// 輸出:0, 2, 4, 6, 8

// 遍歷陣列
const fruits = ["蘋果", "香蕉", "橘子"];
for (let i = 0; i < fruits.length; i++) {
  console.log(`${i + 1}. ${fruits[i]}`);
}
// 輸出:
// 1. 蘋果
// 2. 香蕉
// 3. 橘子

// 巢狀迴圈(九九乘法表)
for (let i = 1; i <= 9; i++) {
  for (let j = 1; j <= 9; j++) {
    console.log(`${i} x ${j} = ${i * j}`);
  }
}

while 迴圈

當不確定要重複幾次,只知道終止條件時使用。

// 基本語法
let count = 0;
while (count < 5) {
  console.log(count);
  count++;
}
// 輸出:0, 1, 2, 3, 4

// 實用範例:密碼驗證
let password = "";
let attempts = 0;
const correctPassword = "1234";

while (password !== correctPassword && attempts < 3) {
  // 這裡通常會用 prompt() 讓使用者輸入
  password = "1234";  // 模擬輸入
  attempts++;
}

if (password === correctPassword) {
  console.log("登入成功");
} else {
  console.log("嘗試次數過多");
}

// 無窮迴圈(小心使用)
// while (true) {
//   // 除非有 break,否則會永遠執行
// }

do...while 迴圈

至少會執行一次,然後才檢查條件。

// 基本語法
let i = 0;
do {
  console.log(i);
  i++;
} while (i < 5);
// 輸出:0, 1, 2, 3, 4

// 與 while 的差異
let x = 10;

while (x < 5) {
  console.log("這行不會執行");  // 條件不符,不執行
}

do {
  console.log("這行會執行一次");  // 至少執行一次
} while (x < 5);

// 實用範例:選單系統
let choice;
do {
  console.log("1. 選項一");
  console.log("2. 選項二");
  console.log("0. 離開");
  // choice = prompt("請選擇:");
  choice = "0";  // 模擬輸入
} while (choice !== "0");

for...of 迴圈

遍歷可迭代物件(陣列、字串等)的值。

// 遍歷陣列
const fruits = ["蘋果", "香蕉", "橘子"];

for (const fruit of fruits) {
  console.log(fruit);
}
// 輸出:蘋果、香蕉、橘子

// 遍歷字串
const str = "Hello";
for (const char of str) {
  console.log(char);
}
// 輸出:H、e、l、l、o

// 與傳統 for 迴圈比較
// 傳統方式
for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

// for...of 方式(更簡潔)
for (const fruit of fruits) {
  console.log(fruit);
}

// 使用解構
const users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 30 }
];

for (const { name, age } of users) {
  console.log(`${name} is ${age} years old`);
}

// 使用 entries() 取得索引
for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

for...in 迴圈

遍歷物件的可列舉屬性。

// 遍歷物件
const user = {
  name: "Charlie",
  age: 28,
  city: "Taipei"
};

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}
// 輸出:
// name: Charlie
// age: 28
// city: Taipei

// 遍歷陣列(不推薦)
const arr = ["a", "b", "c"];
for (const index in arr) {
  console.log(index);  // 輸出:0, 1, 2(索引,不是值)
}

// 比較:for...of vs for...in
const numbers = [10, 20, 30];

for (const num of numbers) {
  console.log(num);  // 10, 20, 30(值)
}

for (const index in numbers) {
  console.log(index);  // 0, 1, 2(索引)
}

// 建議:
// - 物件用 for...in
// - 陣列用 for...of 或陣列方法

break 和 continue

控制迴圈執行流程。

break:立即跳出迴圈

// 找到第一個偶數就停止
for (let i = 1; i <= 10; i++) {
  if (i % 2 === 0) {
    console.log(`找到第一個偶數:${i}`);
    break;  // 跳出迴圈
  }
}
// 輸出:找到第一個偶數:2

// 巢狀迴圈中的 break
for (let i = 1; i <= 3; i++) {
  for (let j = 1; j <= 3; j++) {
    if (j === 2) {
      break;  // 只跳出內層迴圈
    }
    console.log(`i=${i}, j=${j}`);
  }
}

// 搜尋範例
const users = ["Alice", "Bob", "Charlie", "David"];
const target = "Charlie";
let found = false;

for (const user of users) {
  if (user === target) {
    console.log(`找到使用者:${user}`);
    found = true;
    break;
  }
}

if (!found) {
  console.log("找不到使用者");
}

continue:跳過本次迭代

// 只印出奇數
for (let i = 1; i <= 10; i++) {
  if (i % 2 === 0) {
    continue;  // 跳過偶數
  }
  console.log(i);
}
// 輸出:1, 3, 5, 7, 9

// 過濾無效資料
const scores = [85, null, 92, undefined, 88, NaN, 76];

for (const score of scores) {
  if (score === null || score === undefined || isNaN(score)) {
    continue;  // 跳過無效資料
  }
  console.log(score);
}
// 輸出:85, 92, 88, 76

// 跳過特定條件
const words = ["apple", "banana", "apricot", "cherry"];

for (const word of words) {
  if (!word.startsWith("a")) {
    continue;  // 跳過不是 a 開頭的字
  }
  console.log(word);
}
// 輸出:apple, apricot

陣列迭代方法(複習與比較)

雖然不是傳統迴圈,但經常用來取代迴圈。

const numbers = [1, 2, 3, 4, 5];

// forEach:遍歷
numbers.forEach(num => {
  console.log(num);
});

// map:轉換
const doubled = numbers.map(num => num * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

// filter:過濾
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens);  // [2, 4]

// reduce:歸納
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum);  // 15

// 何時使用傳統迴圈 vs 陣列方法?
// 傳統迴圈:需要提早跳出(break)、複雜的流程控制
// 陣列方法:簡單的資料處理、函數式程式設計風格

實戰練習

練習 1:找出質數

function isPrime(n) {
  if (n <= 1) return false;
  if (n === 2) return true;

  for (let i = 2; i <= Math.sqrt(n); i++) {
    if (n % i === 0) {
      return false;
    }
  }
  return true;
}

// 找出 1 到 50 的所有質數
const primes = [];
for (let i = 1; i <= 50; i++) {
  if (isPrime(i)) {
    primes.push(i);
  }
}
console.log(primes);
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

練習 2:反轉字串

function reverseString(str) {
  let reversed = "";
  for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
  }
  return reversed;
}

console.log(reverseString("Hello"));  // "olleH"

// 或使用 for...of
function reverseString2(str) {
  let reversed = "";
  for (const char of str) {
    reversed = char + reversed;
  }
  return reversed;
}

練習 3:階乘計算

function factorial(n) {
  let result = 1;
  for (let i = 1; i <= n; i++) {
    result *= i;
  }
  return result;
}

console.log(factorial(5));   // 120 (5 * 4 * 3 * 2 * 1)
console.log(factorial(10));  // 3628800

練習 4:費氏數列

function fibonacci(n) {
  const sequence = [0, 1];

  for (let i = 2; i < n; i++) {
    sequence[i] = sequence[i - 1] + sequence[i - 2];
  }

  return sequence;
}

console.log(fibonacci(10));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

練習 5:星號圖案

// 直角三角形
function printTriangle(height) {
  for (let i = 1; i <= height; i++) {
    let line = "";
    for (let j = 1; j <= i; j++) {
      line += "*";
    }
    console.log(line);
  }
}

printTriangle(5);
// *
// **
// ***
// ****
// *****

// 金字塔
function printPyramid(height) {
  for (let i = 1; i <= height; i++) {
    let spaces = " ".repeat(height - i);
    let stars = "*".repeat(2 * i - 1);
    console.log(spaces + stars);
  }
}

printPyramid(5);
//     *
//    ***
//   *****
//  *******
// *********

練習 6:兩數之和

// 找出陣列中兩個數字相加等於目標值的組合
function twoSum(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === target) {
        return [i, j];
      }
    }
  }
  return null;
}

const numbers = [2, 7, 11, 15];
console.log(twoSum(numbers, 9));  // [0, 1](2 + 7 = 9)
console.log(twoSum(numbers, 26)); // [2, 3](11 + 15 = 26)

練習 7:資料處理

const students = [
  { name: "Alice", score: 85 },
  { name: "Bob", score: 92 },
  { name: "Charlie", score: 78 },
  { name: "David", score: 95 },
  { name: "Eve", score: 88 }
];

// 計算平均分數
let totalScore = 0;
for (const student of students) {
  totalScore += student.score;
}
const average = totalScore / students.length;
console.log(`平均分數:${average}`);  // 87.6

// 找出最高分的學生
let topStudent = students[0];
for (const student of students) {
  if (student.score > topStudent.score) {
    topStudent = student;
  }
}
console.log(`最高分:${topStudent.name} - ${topStudent.score}`);
// 最高分:David - 95

// 分級統計
const grades = { A: 0, B: 0, C: 0, D: 0, F: 0 };

for (const student of students) {
  if (student.score >= 90) grades.A++;
  else if (student.score >= 80) grades.B++;
  else if (student.score >= 70) grades.C++;
  else if (student.score >= 60) grades.D++;
  else grades.F++;
}

console.log(grades);  // { A: 2, B: 2, C: 1, D: 0, F: 0 }

效能考量

const arr = new Array(1000000).fill(0).map((_, i) => i);

// 不好:每次都計算 length
console.time("bad");
for (let i = 0; i < arr.length; i++) {
  // 處理資料
}
console.timeEnd("bad");

// 好:快取 length
console.time("good");
const len = arr.length;
for (let i = 0; i < len; i++) {
  // 處理資料
}
console.timeEnd("good");

// 更好:for...of(語法簡潔且效能好)
console.time("better");
for (const item of arr) {
  // 處理資料
}
console.timeEnd("better");

// 注意:現代 JavaScript 引擎已經很聰明
// 在大多數情況下,可讀性比微小的效能差異更重要

迴圈選擇指南

// 已知重複次數 → for
for (let i = 0; i < 10; i++) {
  // 執行 10 次
}

// 不確定次數,只知道終止條件 → while
while (condition) {
  // 直到條件為 false
}

// 至少執行一次 → do...while
do {
  // 至少執行一次
} while (condition);

// 遍歷陣列的值 → for...of
for (const item of array) {
  // 處理每個元素
}

// 遍歷物件的屬性 → for...in
for (const key in object) {
  // 處理每個屬性
}

// 簡單的陣列處理 → 陣列方法
array.forEach(item => {
  // 處理每個元素
});

常見錯誤

1. 無窮迴圈

// 錯誤:忘記遞增
let i = 0;
while (i < 5) {
  console.log(i);
  // 忘記 i++,會無限執行
}

// 正確
let i = 0;
while (i < 5) {
  console.log(i);
  i++;
}

2. 陣列越界

const arr = [1, 2, 3];

// 錯誤:使用 <= 會超出範圍
for (let i = 0; i <= arr.length; i++) {
  console.log(arr[i]);  // 最後一次會是 undefined
}

// 正確:使用 <
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
}

3. 迴圈中修改陣列

const numbers = [1, 2, 3, 4, 5];

// 危險:迴圈中刪除元素
for (let i = 0; i < numbers.length; i++) {
  if (numbers[i] % 2 === 0) {
    numbers.splice(i, 1);  // 會跳過元素
  }
}

// 正確:倒著遍歷
for (let i = numbers.length - 1; i >= 0; i--) {
  if (numbers[i] % 2 === 0) {
    numbers.splice(i, 1);
  }
}

// 更好:使用 filter
const odds = numbers.filter(n => n % 2 !== 0);

小結

這篇我們學習了:

  • for、while、do...while 迴圈
  • for...of 和 for...in 的差異
  • break 和 continue 的使用
  • 迴圈的實戰應用
  • 效能考量與最佳實踐

下一步:

完成這篇後,你已經能夠: - 選擇適合的迴圈類型 - 處理重複性任務 - 避免常見的迴圈錯誤

前往下一篇:單元八:作用域與閉包

在那裡,我們會學習變數的可見範圍與進階概念。


0 留言

目前沒有留言

發表留言
回覆