JavaScript 条件与循环

程序最强大的地方,在于它能"根据情况做不同的事"(条件)和"重复干活"(循环)。这两类语句是编程的核心,这一章我们一次讲清楚。

1. if-else 条件语句

最基本的分支语句。条件为真就执行对应分支:

// if / else if / else
const score = 78;
if (score >= 90) {
  console.log("优秀");
} else if (score >= 60) {
  console.log("及格");
} else {
  console.log("不及格");
}
// 输出: 及格

// 条件可以嵌套
const age = 20;
const hasTicket = true;
if (age >= 18) {
  if (hasTicket) {
    console.log("欢迎入场");
  } else {
    console.log("请先购票");
  }
}

// 单行 if 也可以省 {},但不推荐(可读性差)
if (true) console.log("ok");

注意:JS 的 if 判断时会把条件转成布尔——所以 if (0)if ("")if (null) 都是 false,而 if ("0")if ([]) 反而是 true(非空字符串和对象是真值)。

2. switch 多分支

当条件是"等于某个值",而且分支特别多时,用 switchif-else 链更清晰。但有个大坑:必须写 break!

// switch:多分支选择,匹配 case 后,会一直执行直到 break
const day = "周三";
switch (day) {
  case "周一":
  case "周二":
  case "周三":
  case "周四":
  case "周五":
    console.log("工作日");
    break;              // 必须写,否则会"穿透"到下面
  case "周六":
  case "周日":
    console.log("周末");
    break;
  default:
    console.log("未知");
}
// 输出: 工作日

// ⚠️ 忘记 break 会"穿透"——执行后续所有 case 直到 break
const x = 1;
switch (x) {
  case 1:
    console.log("一");
  case 2:
    console.log("二");
  case 3:
    console.log("三");
    break;
}
// 输出: 一 二 三(穿透了!)

switch 的"穿透"(fall-through)特性有时被故意利用——比如多个 case 共享同一段代码(上面"周一"到"周五"都执行同一段)。但99% 的情况下是忘记写 break 导致的 bug。新项目里其实更推荐用对象映射代替 switch。

另外 switch 用的是 === 严格比较,不会做类型转换。

3. for 循环

经典的 C 风格 for 循环,适合已知次数的循环:

// 经典 for 循环:初始化;条件;更新
for (let i = 0; i < 3; i++) {
  console.log(`第 ${i} 次`);
}
// 输出: 第 0 次 / 第 1 次 / 第 2 次

// 累加求和
let sum = 0;
for (let i = 1; i <= 100; i++) {
  sum += i;
}
console.log(sum);   // 5050

// 倒序
for (let i = 3; i > 0; i--) {
  console.log(i);   // 3 / 2 / 1
}

// 省略任意一段(但要用 break 防止死循环)
let n = 0;
for (;;) {
  if (n >= 3) break;
  console.log(n);
  n++;
}

循环变量用 let 不能用 var(后者会泄漏到外层作用域,前面变量章节讲过)。如果循环体里用了 setTimeout 这类异步,务必用 let 才能正确捕获每轮的值。

4. while 与 do-while

当循环次数不确定、靠"条件满足才结束"时,用 while:

// while:先判断后执行(可能一次都不执行)
let i = 0;
while (i < 3) {
  console.log(i);
  i++;
}
// 输出: 0 / 1 / 2

// do-while:先执行后判断(至少执行一次)
let j = 0;
do {
  console.log(j);
  j++;
} while (j < 3);

// 实战:游戏循环、用户输入直到合法
let input;
do {
  // input = prompt("请输入 yes 或 no");
  input = "yes";  // 假设用户输入
} while (input !== "yes" && input !== "no");

注意别写出死循环(条件永远为真),会让页面卡死。常用的退出条件是用 break 或在条件里加边界。

5. break 和 continue

// break:跳出整个循环
for (let i = 0; i < 10; i++) {
  if (i === 3) break;     // i=3 时整个循环结束
  console.log(i);
}
// 输出: 0 / 1 / 2

// continue:跳过本轮,进入下一轮
for (let i = 0; i < 5; i++) {
  if (i % 2 === 0) continue;   // 跳过偶数
  console.log(i);
}
// 输出: 1 / 3

// 嵌套循环 + 标签 break(跳出外层)
outer:
for (let i = 0; i < 3; i++) {
  for (let j = 0; j < 3; j++) {
    if (i === 1 && j === 1) break outer;
    console.log(`(${i},${j})`);
  }
}
// 输出: (0,0) (0,1) (0,2) (1,0)

JS 还支持标签 break(上面例子里的 break outer),用来跳出多层嵌套循环。但实际开发用得不多——通常是把内层循环抽成一个函数,直接 return

6. for-of:遍历数组的首选

ES6 引入的 for-of 是遍历可迭代对象(数组、字符串、Map、Set)的现代方式:

// for-of:遍历"可迭代对象"(数组、字符串、Map、Set),ES6
const arr = ["a", "b", "c"];
for (const item of arr) {
  console.log(item);
}
// 输出: a / b / c

// 想同时拿索引,用 entries()
for (const [index, item] of arr.entries()) {
  console.log(index, item);
}
// 输出: 0 a / 1 b / 2 c

// 遍历字符串
for (const ch of "hi") {
  console.log(ch);   // h / i
}

// for-in:遍历对象的"可枚举属性"(包括继承的)
const user = { name: "小明", age: 20 };
for (const key in user) {
  console.log(key, user[key]);
}
// 输出: name 小明 / age 20

// ⚠️ for-in 不适合遍历数组:它会遍历所有可枚举属性,顺序不保证
// 数组永远用 for-of 或 forEach,不要用 for-in

for-of vs for-in 一句话区分:

7. 实战:遍历数组的几种方式

const nums = [10, 20, 30];

// 1. for-of(最推荐,简洁)
for (const n of nums) {
  console.log(n);
}

// 2. 经典 for(需要索引时用)
for (let i = 0; i < nums.length; i++) {
  console.log(i, nums[i]);
}

// 3. forEach(简单遍历,但不能 break)
nums.forEach((n, i) => {
  console.log(i, n);
});

// 4. map(把每个元素转成新数组)
const doubled = nums.map(n => n * 2);
console.log(doubled);  // [20, 40, 60]

// 5. filter(筛选满足条件的)
const big = nums.filter(n => n > 15);
console.log(big);  // [20, 30]

// 6. reduce(汇总成一个值)
const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum);  // 60

现代 JS 中,mapfilterreduce 用得极多,后面"数组"一章会详讲。

8. 常见陷阱

小结

下一篇我们看 JS 的函数——把代码封装成可复用的"积木块"。

← 上一篇 JavaScript 运算符

下一篇 JavaScript 函数

✈️💬