JavaScript 条件判断与循环

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

Node.js 22 LTS

条件与循环的语法本身很短,但选错写法会带来「遍历结果不对」「break 不生效」「遍历到了原型上的属性」这类问题。这一篇讲选择依据。

if / else 与三元表达式

const score = 85;

if (score >= 90) {
  console.log("优秀");
} else if (score >= 60) {
  console.log("及格");
} else {
  console.log("不及格");
}

只有两个分支且都用于取值时,三元表达式更紧凑:

const label = score >= 60 ? "及格" : "不及格";

嵌套三元超过一层就该换回 if,可读性优先。

switch 适合等值匹配

const status = "pending";

switch (status) {
  case "pending":
    console.log("等待处理");
    break;
  case "done":
    console.log("已完成");
    break;
  default:
    console.log("未知状态");
}

两个必须记住的点:

  1. switch 用的是 === 比较,"1" 不匹配 1。
  2. 每个 case 结尾必须写 break,否则会继续执行下一个 case(称为 fall-through)。刻意的 fall-through 要写注释说明。

for:需要索引时用

const names = ["ann", "bob", "cindy"];

for (let i = 0; i < names.length; i++) {
  console.log(i, names[i]);
}

缓存数组长度(const len = names.length)在现代引擎里已经没有明显收益,不必为性能牺牲可读性。

for…of:只取值时用(推荐)

for (const name of names) {
  console.log(name);
}

for...of 遍历的是值,适用于数组、字符串、Set、Map 等可迭代对象。它比 for 更短,也不会出现索引越界。

需要索引时配合 entries():

for (const [i, name] of names.entries()) {
  console.log(i, name);
}

for…in:不要用来遍历数组

for...in 遍历的是可枚举的属性名(键),并且会包含原型链上的属性:

const arr = ["a", "b"];
arr.custom = "x";

for (const key in arr) {
  console.log(key);   // "0", "1", "custom"
}

遍历数组用 for...of 或数组方法;for...in 只在遍历普通对象的键时才考虑,且通常配合 Object.hasOwn() 过滤:

const user = { name: "ann", age: 30 };

for (const key in user) {
  if (Object.hasOwn(user, key)) {
    console.log(key, user[key]);
  }
}

更推荐直接用 Object.keys(user) / Object.entries(user) 转成数组后遍历,见本系列第 6 篇。

while 与 do…while

while 用于次数不确定的循环:

let retry = 0;
while (retry < 3) {
  console.log(`第 ${retry + 1} 次尝试`);
  retry++;
}

do...while 保证至少执行一次,适合「先做一次,再判断是否继续」的场景,例如「先读取一次输入,输入不合法就重读」。

忘记更新循环变量会造成死循环,写 while 时先确认退出条件一定会到达。

break 与 continue

for (const n of [1, 2, 3, 4, 5]) {
  if (n === 2) continue;   // 跳过本次,继续下一轮
  if (n === 4) break;      // 直接结束整个循环
  console.log(n);
}
// 输出:1 3

需要注意:forEach 里 break 和 continue 都是非法的。

[1, 2, 3].forEach((n) => {
  if (n === 2) break;      // SyntaxError: Illegal break statement
});

forEach 的回调里想跳过某项只能 return(相当于 continue);想提前结束整个遍历,改用 for...of + break,或用 some() / every() / find() 这类自带短路语义的方法。

标签:跳出嵌套循环

outer:
for (const row of [[1, 2], [3, 4]]) {
  for (const n of row) {
    if (n === 3) break outer;   // 直接跳出两层
    console.log(n);
  }
}
// 输出:1 2

标签语法少见,但确有多层跳出需求时比「设置标志变量」干净得多。

一个完整例子:统计与查找

const orders = [
  { id: 1, amount: 120, paid: true },
  { id: 2, amount: 80, paid: false },
  { id: 3, amount: 200, paid: true },
];

let total = 0;
let firstUnpaid = null;

for (const order of orders) {
  if (!order.paid) {
    firstUnpaid ??= order;   // 只记录第一个未支付订单
    continue;
  }
  total += order.amount;
}

console.log(total);          // 320
console.log(firstUnpaid.id); // 2

常见错误与注意事项

  1. switch 忘记 break:会连续执行后续分支,结果是「同时命中多个」。
  2. 用 for...in 遍历数组:会拿到字符串类型的下标和额外属性,排序也不保证。
  3. 在 forEach 里写 break:语法错误,用 for...of 或 some() / find()。
  4. while 循环变量没更新:死循环,脚本卡死。
  5. 在遍历数组的同时 splice 删除元素:下标会错位,漏掉元素。用 filter() 生成新数组。

小结

  • 要索引用 for 或 entries(),只要值用 for...of。
  • for...in 遍历键,不用于数组。
  • switch 是 === 比较,记得 break。
  • forEach 不能中断,需要中断就用 for...of 或 some() / find()。

下一步