switch语句用法使用教程:新手完成第一张作品的方法

AI绘画1小时前发布 jdkjadmin
0 00
switch语句用法使用教程:新手完成第一张作品的方法

switch语句用法使用教程:新手完成第一张作品的方法

当程序要根据一个值,在几种明确的情况中选择不同操作时,switch 往往比一长串 if...else if 更容易阅读。它常用于菜单选项、命令类型、状态码、星期或季节等场景。

本文以 JavaScript 为例,从基本语法讲起,最后完成一个可以在 Node.js 中运行的“文字版四季建议器”。需要特别记住的是:JavaScript 的 switch 使用严格相等比较,case 默认不会自动停止执行,输入值也不会因为写了 switch 就变得可信。理解这些边界,才能写出既正确又容易维护的分支代码。

一、switch 适合解决什么问题

假设程序收到一个表示方向的值:"north"、"south"、"east" 或 "west"。每个值都对应一条处理路径。用多个条件可以写成:

const direction = "north";

if (direction === "north") {
  console.log("向北");
} else if (direction === "south") {
  console.log("向南");
} else if (direction === "east") {
  console.log("向东");
} else if (direction === "west") {
  console.log("向西");
} else {
  console.log("未知方向");
}

条件数量增加后,重复的变量名和比较符会让代码变长。这种“同一个值与多个候选值逐一比较”的结构,正是 switch 的典型使用场景:

const direction = "north";

switch (direction) {
  case "north":
    console.log("向北");
    break;
  case "south":
    console.log("向南");
    break;
  case "east":
    console.log("向东");
    break;
  case "west":
    console.log("向西");
    break;
  default:
    console.log("未知方向");
}

当每个分支都围绕同一个待判断值展开,而且候选情况清楚、彼此离散时,switch 通常很合适。如果分支条件是范围判断、多个变量组合,或需要复杂逻辑表达,if...else 往往更直观。

二、基本语法与执行规则

switch 的基本结构如下:

switch (表达式) {
  case 候选值1:
    // 候选值1匹配时执行
    break;
  case 候选值2:
    // 候选值2匹配时执行
    break;
  default:
    // 没有任何 case 匹配时执行
}

执行过程可以概括为:

  1. 计算一次 switch 后括号中的表达式。
  2. 按代码中的顺序检查 case 值,并与这个结果进行严格相等比较。
  3. 找到匹配项后,从对应的 case 开始执行后续语句。
  4. 遇到 break、return、抛出异常等控制流结束方式,或执行到整个 switch 末尾时,停止当前流程。
  5. 如果没有匹配项,执行 default;没有写 default 时,则不执行任何分支。

括号里的表达式可以是变量,也可以是函数调用或其他表达式。例如 switch (getStatus()) 会调用一次 getStatus()。为了让分支行为容易推断,通常建议把判断值先存入变量,并避免在 case 表达式中放有副作用的操作。

三、关键边界:比较使用严格相等

JavaScript 的 switch 使用严格相等(===)来比较判断值与 case 值,不会自动进行类型转换。因此,1 与 "1" 不匹配,true 与 1 也不匹配:

const value = "1";

switch (value) {
  case 1:
    console.log("数字 1");
    break;
  case "1":
    console.log("字符串 1");
    break;
  default:
    console.log("其他值");
}

运行结果是“字符串 1”。如果数据来自输入框、网址参数或命令行参数,要先明确它应该是什么类型,再做校验和转换。不要为了让分支“碰巧匹配”而混用字符串与数字。

对象和数组也按引用比较,不会因为内容看起来相同就相等:

switch ([1, 2]) {
  case [1, 2]:
    console.log("不会执行");
    break;
  default:
    console.log("数组不是同一个对象");
}

两个数组字面量会创建不同对象,因此不相等。需要比较数组或对象的内容时,应使用专门的内容比较逻辑,而不是期望 switch 自动逐项比较。

还有一个容易忽略的情况:NaN 与自身严格比较也不相等,所以 switch (NaN) 不会匹配 case NaN。如果要判断一个值是否为 NaN,应使用 Number.isNaN(value) 等方式,而不是用 switch 的 case NaN。

四、break 和贯穿执行

break 用来退出当前 switch。如果匹配后没有 break,程序会继续执行后面的语句,包括下一个 case 下的语句,这种行为叫作贯穿执行(fall-through)。

const level = 2;

switch (level) {
  case 1:
    console.log("初级");
  case 2:
    console.log("中级");
  case 3:
    console.log("高级");
}

这段代码匹配到 case 2 后,会打印“中级”和“高级”。如果每种情况只应该执行自己的操作,就在分支结束处写 break:

const level = 2;

switch (level) {
  case 1:
    console.log("初级");
    break;
  case 2:
    console.log("中级");
    break;
  case 3:
    console.log("高级");
    break;
}

有时贯穿执行是有意设计的,例如多个输入对应同一项操作:

const day = "星期六";

switch (day) {
  case "星期六":
  case "星期日":
    console.log("休息日");
    break;
  default:
    console.log("工作日");
}

这里第一个 case 没有语句,执行会继续进入下一个标签;两个日期都会到达同一段代码。为了减少误读,可以在共享分支时把标签排在一起,并添加简短注释,说明这里是刻意贯穿,而不是漏写 break。

break 只退出最近的 switch 或循环,不会自动退出函数。如果分支里调用了其他函数,函数内部的 break 也不能用来退出外层 switch。

五、default 的位置与作用

default 表示没有任何 case 匹配时采用的分支,通常放在最后,方便读者理解:

switch (status) {
  case "ready":
    console.log("可以开始");
    break;
  case "running":
    console.log("正在运行");
    break;
  default:
    console.log("状态未知");
}

语法上,default 不一定必须放在最后;但它如果位于中间,匹配到它或从它前面的标签贯穿下来时,都可能继续执行后续语句。因此初学时建议把它放在最后,并根据需要用 break 明确结束。

default 不是输入校验的替代品。它可以处理“当前代码没有为该值定义分支”的情况,却无法证明这个值合法,也不能自动防止异常数据影响程序。

六、case 后面可以写什么

case 后面可以是表达式,但它要与 switch 的结果进行严格相等比较。因此,下面这种写法不会用来检查范围:

const score = 85;

switch (score) {
  case score >= 60:
    console.log("及格");
    break;
  default:
    console.log("不及格");
}

score >= 60 计算出的结果是布尔值 true,程序实际比较的是 85 === true,因此并不匹配。判断区间应使用 if...else:

const score = 85;

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

case 表达式会按需求值,直到找到匹配项为止。虽然可以写函数调用或其他表达式,但为了便于审查和调试,建议使用简单、稳定的字面值或常量,避免让表达式改变程序状态。

七、switch 中的变量作用域

switch 的一对花括号形成代码块。同一个 switch 中,各个 case 标签并不会自动各自创建一个独立的词法作用域。若在不同分支中用 let 或 const 声明同名变量,可能触发重复声明错误。

switch (action) {
  case "add":
    const message = "新增";
    console.log(message);
    break;
  case "remove":
    const message = "删除";
    console.log(message);
    break;
}

这段代码会因为重复声明 message 而报错。可以为每个分支添加花括号建立独立作用域:

switch (action) {
  case "add": {
    const message = "新增";
    console.log(message);
    break;
  }
  case "remove": {
    const message = "删除";
    console.log(message);
    break;
  }
  default:
    console.log("未知操作");
}

简单分支通常不需要额外的块;当分支里要声明局部变量,尤其是不同分支会使用同名变量时,给分支加花括号是清晰可靠的做法。

八、完成第一张作品:文字版四季建议器

下面做一个小型命令行程序:用户输入春、夏、秋、冬,程序给出相应建议;输入其他内容时显示帮助信息。这个例子包含输入读取、简单规范化、switch 分支和默认处理,可以作为一份完整的入门作品。

运行环境

示例使用 Node.js,不需要安装第三方依赖。先在终端执行 node --version,确认已经安装 Node.js。把下面的代码保存为 seasons.js:

const rawSeason = process.argv[2] ?? "";
const season = rawSeason.trim();

switch (season) {
  case "春":
    console.log("春天:适合出门散步,留意天气变化。");
    break;
  case "夏":
    console.log("夏天:记得补水,避开长时间暴晒。");
    break;
  case "秋":
    console.log("秋天:早晚温差可能较大,出门可以带件外套。");
    break;
  case "冬":
    console.log("冬天:注意保暖,出行前查看天气。");
    break;
  default:
    console.log("请输入一个季节:春、夏、秋或冬。");
}

在保存文件的目录打开终端,运行:

node seasons.js 春

预期输出:

春天:适合出门散步,留意天气变化。

也可以分别运行 node seasons.js 夏、node seasons.js 秋 或 node seasons.js 冬,查看其他结果。不传参数或传入不支持的值时,程序会进入 default 分支。

逐段读懂这份作品

process.argv 是 Node.js 提供的命令行参数数组。运行 node seasons.js 春 时,用户输入的“春”通常位于 process.argv[2]。命令行参数以字符串形式提供,因此本例处理的是字符串,而不是季节编号。

?? "" 是空值合并运算符:当左侧为 null 或 undefined 时,使用右侧的空字符串作为默认值。它不会把数字 0 或空字符串等所有“假值”都替换掉。这里即使没有提供参数,rawSeason 也会是字符串,后续调用 trim() 不会因为缺少参数而报错。

trim() 会移除字符串两端的空白字符。因此,用户输入 " 春 " 时,程序会将它整理为 "春",再交给 switch 判断。它不会移除字符串中间的空格,也不会自动把繁体字、英文季节名或其他说法转换成中文标准值。

每个分支都以 break 结束,所以命中一个季节后,不会接着打印其他季节的建议。default 则为未列出的输入提供明确反馈,而不是让程序静默结束。

给作品增加输入校验

这个示例已经通过 default 对未知季节做出回应。如果要让输入规则更明确,可以先把允许值列出来,再判断输入是否在白名单中:

const rawSeason = process.argv[2] ?? "";
const season = rawSeason.trim();
const allowedSeasons = ["春", "夏", "秋", "冬"];

if (!allowedSeasons.includes(season)) {
  console.log("输入无效。可选值:春、夏、秋、冬。");
} else {
  switch (season) {
    case "春":
      console.log("春天:适合出门散步,留意天气变化。");
      break;
    case "夏":
      console.log("夏天:记得补水,避开长时间暴晒。");
      break;
    case "秋":
      console.log("秋天:早晚温差可能较大,出门可以带件外套。");
      break;
    case "冬":
      console.log("冬天:注意保暖,出行前查看天气。");
      break;
  }
}

在这个特定例子里,外层白名单检查与 default 有部分重复:如果保留了 default,通常没有必要仅为相同目的再做一次检查。但真实程序可能要在进入分支前拒绝输入、记录错误或返回错误状态,此时先校验再分派就有意义。要根据程序的实际需求选择结构,避免重复检查让逻辑变得难懂。

九、常见错误与排查方式

1. 忘记 break,导致多条分支连续执行

表现是命中一个选项后,后续分支的内容也出现。逐个检查分支结尾是否需要 break。如果确实希望多个标签共享操作,就把这种贯穿明确地写出来。

2. 把 case 当作范围判断

case score > 60 不是“当分数大于 60 时”的语法,它会拿一个布尔值与判断值进行比较。区间和复合条件用 if...else 更合适。

3. 混淆数字与字符串

例如从命令行取得的 "2" 不等于数字 2。先确定输入类型;如果需要数字,应转换并验证转换结果,再用数字进行比较。

const rawValue = process.argv[2] ?? "";
const numberValue = Number(rawValue);

if (!Number.isFinite(numberValue)) {
  console.log("请输入有效数字。");
} else {
  switch (numberValue) {
    case 1:
      console.log("选择一");
      break;
    case 2:
      console.log("选择二");
      break;
    default:
      console.log("没有这个选项");
  }
}

注意,Number("") 会得到 0,因此若空输入不应被解释为零,还要在转换前单独检查空字符串或缺少输入的情况。

4. 忘记 default

没有 default 并不一定是错误:如果所有未匹配值都应当什么也不做,可以不写。但对于用户输入、外部数据或状态分派,最好考虑未知值出现时应该怎样处理。是否要记录、提示、拒绝或交由上层处理,应按业务需求决定。

5. 分支声明同名变量而触发错误

当多个分支声明同名的 let 或 const 时,给分支加花括号建立独立作用域;或者把声明提到合适的公共作用域并使用不同名称。不要用 var 来回避问题,因为它的作用域规则不同,可能引入更难察觉的行为。

6. 忽略输入中的空格或大小写

字符串匹配要求值完全符合预期。对于英文命令,可以按需求先做 trim(),再统一大小写;对于本例的中文季节,大小写转换没有意义。不要假设 switch 会自动清理或规范化输入。

十、什么时候不该用 switch

switch 并不是所有条件判断的通用替代品。下面这些情况通常使用其他结构更合适:

  • 判断区间:例如分数等级、年龄范围、温度阈值,使用 if...else 直接表达大小关系。
  • 条件由多个值共同决定:例如同时判断用户角色、资源状态和操作类型,if 或独立函数往往更清楚。
  • 分支主要是键到函数的映射:可以考虑对象映射或 Map,但仍要谨慎处理未知键和对象原型等问题。
  • 各分支逻辑非常复杂:可以把具体操作拆成函数,让分派部分保持简短。

例如,把操作名映射到函数:

const actions = {
  greet() {
    console.log("你好");
  },
  help() {
    console.log("可用操作:greet、help");
  }
};

const actionName = process.argv[2] ?? "";
const action = actions[actionName];

if (typeof action === "function") {
  action();
} else {
  console.log("未知操作");
}

这是一种可选写法,不代表它在所有情况下都优于 switch。当只有少数几个明确分支时,switch 通常易读;当映射较大、操作可动态扩展时,映射结构可能更方便。

十一、switch 与安全:它不是安全边界

使用 switch 本身不会让程序更安全,也不会自动过滤恶意输入。它只是控制程序走哪一段代码。用户能够提供的值,仍然需要结合数据用途进行校验。

如果程序只接受一组有限的命令或操作,采用白名单思路通常比“凡是不认识的值都猜测处理”更稳妥:明确列出支持的值,其他输入拒绝或返回错误。但白名单是否足够,取决于后续操作和业务规则,不能仅凭一个 switch 就认为安全检查已经完成。

尤其不要把外部输入直接拼进动态代码、命令或数据库查询中。比如,分支只负责选择允许调用的固定函数,比把输入当成代码执行更容易审查。对文件路径、权限、金额、身份认证等敏感操作,还需要针对具体风险进行独立校验和授权;switch 不能替代这些措施。

也不建议为了“防御所有情况”就在每个 case 里重复写大量相同的检查。更好的做法通常是分清步骤:先验证输入格式和允许范围,再执行相应分支,最后由对应业务逻辑完成权限和状态检查。

十二、写完后怎样验证

完成代码后,不要只测试一个正常输入。至少检查以下情况:

  • 每个已支持的选项都能进入正确分支。
  • 完全不传参数时,程序会给出清楚提示。
  • 输入一个未知值时,会进入默认处理。
  • 输入前后带空格时,行为符合预期。
  • 大小写、数据类型或空字符串等边界情况有明确处理方式。

四季建议器可以这样快速检查:分别运行四个季节,确认各自只输出一条建议;再运行 node seasons.js 月 和不带参数的命令,确认提示内容正确。若出现多条季节建议,先查找是否遗漏了 break;若合法输入进入了 default,检查传入值是否带空白、类型是否一致,以及实际输入内容是否与 case 完全相同。

结语

掌握 switch,重点不在于记住花括号的样子,而在于理解它的分派规则:判断表达式只计算一次,比较采用严格相等,匹配后可能贯穿到后续分支,default 处理未匹配情况,但不等同于输入校验。遇到范围条件、复杂组合判断或不合适的数据类型时,应该选择更贴合问题的写法。

从一个小型命令行作品开始,逐项测试正常输入和异常输入,再根据实际需要扩展功能,就是不错的练习路径。把分支写清楚、把边界想明白,你就已经迈出了制作第一份 JavaScript 小作品的第一步。

© 版权声明

相关文章