Skip to content

条件判断与循环 ​

上一节已经能判断成绩是否合法。接下来要让无效成绩停止分级,把有效成绩 86 显示为“良好”、52 显示为“需要补考”,还要显示学员状态并统计多名学员的合格人数。本节先处理一名学员的条件分支,再用循环批量处理。

条件分支 ​

if 单分支与双分支 ​

js
const score = 86;

if (score >= 60) {
  console.log("成绩合格");
}

圆括号中是条件,条件为真时执行花括号内的代码。即使当前只有一行,课程仍保留花括号。

plantuml Diagram

if 没有 else 时,条件为假就直接跳过代码块。

如果还要处理不合格的情况,可以增加 else:

js
const score = 52;

if (score >= 60) {
  console.log("成绩合格");
} else {
  console.log("需要补考");
}

两个分支只会执行一个。

plantuml Diagram

成绩分级 ​

js
const scoreText = "86";
const score = Number(scoreText);
const isValidScore =
  scoreText.trim() !== "" && Number.isFinite(score) && score >= 0 && score <= 100;
let level;

if (!isValidScore) {
  level = "成绩无效";
} else if (score >= 90) {
  level = "优秀";
} else if (score >= 80) {
  level = "良好";
} else if (score >= 60) {
  level = "合格";
} else {
  level = "需要补考";
}

console.log(level);

先用上一节的规则排除空值、非数字和超出范围的成绩,再进行分级。把 scoreText 改为 "86分" 或 " ",应输出“成绩无效”,而不是误判为“需要补考”。条件从上到下检查,遇到第一个为真的分支后停止;成绩范围从高到低写,避免 95 先被 score >= 60 拦截。

下图把“从上到下、命中即停止”展开了。一旦某个条件成立,程序就不会再进入后面的分支。

plantuml Diagram

switch 状态匹配 ​

当一个值需要与多个明确选项比较时,可以使用 switch:

js
const status = "pending";
let statusText;

switch (status) {
  case "active":
    statusText = "正常";
    break;
  case "pending":
    statusText = "待审核";
    break;
  case "disabled":
    statusText = "已停用";
    break;
  default:
    statusText = "未知状态";
}

console.log(statusText);

break 结束当前 switch,阻止继续执行后面的 case。default 处理没有匹配的值。匹配时不会隐式转换类型,例如数字 1 不会匹配字符串 "1"。

plantuml Diagram

每个 case 都是一个固定值入口;break 让当前分支处理完后直接离开 switch。

switch 适合离散的相等比较;成绩范围使用 if...else if 更直接。不要用“谁性能更好”作为基础选择依据,先看表达是否清楚。

三元表达式 ​

简单的二选一可以写成:

js
const score = 86;
const resultText = score >= 60 ? "合格" : "不合格";
console.log(resultText); // "合格"
plantuml Diagram

复杂的多层判断不要嵌套多个三元运算符,改用 if 或函数。

循环遍历 ​

for 索引遍历 ​

js
for (let index = 0; index < 3; index += 1) {
  console.log(`当前索引:${index}`);
}

三个部分分别表示:

  1. let index = 0:循环开始前执行一次。
  2. index < 3:每轮开始前判断。
  3. index += 1:每轮结束后执行。

用索引处理数组:

先认识循环所需的最小数组写法:方括号保存一组值,索引从 0 开始,students[0] 读取第一项,students.length 表示项数。数组的新增、删除和其他操作在下一节学习。

js
const students = ["张三", "李四", "王五"];

for (let index = 0; index < students.length; index += 1) {
  console.log(`${index + 1}. ${students[index]}`);
}

for...of 元素遍历 ​

只关心数组元素时,for...of 更简洁:

js
const scores = [86, 72, 93];
let total = 0;

for (const score of scores) {
  total += score;
}

console.log(total);

这里依次读到 86、72、93,total 从 0 变为 86、158、251。每轮声明的 score 只表示当前这一项,不是整个数组。

for...of 每轮得到一个值。不要用 for...in 遍历数组元素;它得到的是可枚举属性名,语义不同。

plantuml Diagram

for...of 会自动取出下一个值,所以不需要自己维护 index。

while 条件循环 ​

不知道确切循环次数,只知道继续条件时使用 while:

js
let remainingSeats = 3;

while (remainingSeats > 0) {
  console.log(`剩余名额:${remainingSeats}`);
  remainingSeats -= 1;
}

必须确保条件最终会变为假,否则会形成无限循环。

for 和 while 的写法不同,但执行都会回到“再次检查条件”。下图使用 for 的索引变量展示这个闭环:

plantuml Diagram

如果忘记 index += 1,条件可能一直为真。while 中同样要有能让条件逐步接近结束的操作。

循环控制 ​

break 终止循环 ​

找到目标后立即结束循环:

下面把姓名和编号放在同一对花括号中,组成一条学员记录;student.id 表示读取当前记录的编号。现在先理解循环怎样逐条查找,记录的完整组织方式放到下一节。

js
const students = [
  { id: 1, name: "张三" },
  { id: 2, name: "李四" },
  { id: 3, name: "王五" },
];

let targetStudent = null;

for (const student of students) {
  if (student.id === 2) {
    targetStudent = student;
    break;
  }
}

console.log(targetStudent);

这里的 break 结束当前 for 循环,前面 switch 中的 break 只结束当前 switch。如果把 switch 写在循环内,switch 的 break 不会结束外层循环。数组后面会学习 find(),但理解循环的 break 有助于读懂一般循环。

continue 跳过当前轮 ​

跳过当前轮次,继续下一轮:

js
const students = [
  { name: "张三", status: "active" },
  { name: "李四", status: "disabled" },
  { name: "王五", status: "active" },
];

for (const student of students) {
  if (student.status !== "active") {
    continue;
  }

  console.log(student.name);
}

break 和 continue 都会改变默认循环流程,但去向不同:

plantuml Diagram
  • break 的出口是“循环之外”。
  • continue 的出口是“下一轮循环”。

学员成绩与状态处理 ​

本例约定成绩已经是 0 到 100 之间的有效数字,非法输入先按上一节的方法处理。合格人数统计所有记录,不按学员状态排除。

js
const students = [
  { name: "张三", score: 86, status: "active" },
  { name: "李四", score: 52, status: "active" },
  { name: "王五", score: 93, status: "disabled" },
  { name: "赵敏", score: 78, status: "pending" },
];

let passedCount = 0;

for (const student of students) {
  let level;

  if (student.score >= 90) {
    level = "优秀";
  } else if (student.score >= 80) {
    level = "良好";
  } else if (student.score >= 60) {
    level = "合格";
  } else {
    level = "需要补考";
  }

  let statusText;

  switch (student.status) {
    case "active":
      statusText = "正常";
      break;
    case "pending":
      statusText = "待审核";
      break;
    case "disabled":
      statusText = "已停用";
      break;
    default:
      statusText = "未知";
  }

  if (student.score >= 60) {
    passedCount += 1;
  }

  console.log(`${student.name}:${level},状态:${statusText}`);
}

console.log(`合格人数:${passedCount}`);

综合案例的外层是“遍历学员”,内层才是成绩和状态判断。先看清一名学员的处理顺序,再理解整个循环:

plantuml Diagram

练习与验收 ​

运行综合案例,应得到张三“良好”、李四“需要补考”、王五“优秀”、赵敏“合格”,合格人数为 3。

  1. 把李四的成绩改为 60,应变为“合格”,合格人数变为 4。
  2. 从原始数据重新开始,在循环开头用 continue 跳过非 active 学员,应只处理张三和李四。此时只统计正常状态学员,其中合格人数为 1。
  3. 将查找示例的目标编号改为不存在的 99,结果应保留为 null。
  4. 在 switch 示例中把状态改为 "archived",应输出“未知状态”;在 while 示例中把 remainingSeats 设为 0,循环体应执行 0 次。

说明循环中的 break 是否结束整个当前循环,以及 continue 跳过了本轮哪些语句。下一节正式学习数组与对象,用同一套结构组织这些学员记录。

小结 ​

  1. if...else 适合范围和组合条件,switch 适合离散相等分支。
  2. 分支顺序会影响结果,数据验证通常放在业务判断之前。
  3. for 适合需要索引的循环,for...of 适合直接读取集合值。
  4. while 要确保条件最终结束。
  5. break 结束当前 switch 或循环,continue 跳过当前轮次剩余语句。