switch

**switch** 语句会评估一个表达式,将表达式的值与一系列 case 子句匹配,并在第一个匹配值的 case 子句之后执行语句,直到遇到 break 语句。switch 语句的 default 子句将在表达式的值与任何 case 不匹配时跳转到。

试试看

语法

js
switch (expression) {
  case caseExpression1:
    statements
  case caseExpression2:
    statements
  // …
  case caseExpressionN:
    statements
  default:
    statements
}
表达式

其结果与每个 case 子句匹配的表达式。

caseExpressionN 可选

用于与 expression 匹配的 case 子句。如果 expression 的值与任何 caseExpressionN 的值匹配,则执行从该 case 子句后的第一个语句开始,直到 switch 语句的结尾或第一个遇到的 break

default 可选

default 子句;如果提供,则在 expression 的值与任何 case 子句不匹配时执行此子句。switch 语句只能有一个 default 子句。

描述

switch 语句首先评估其表达式。然后它查找第一个其表达式评估结果与输入表达式结果相同(使用严格相等比较)的 case 子句,并将控制权转移到该子句,执行该子句后的所有语句。

子句表达式仅在必要时评估 - 如果已找到匹配项,则不会评估后续的 case 子句表达式,即使它们会被贯穿访问。

js
switch (undefined) {
  case console.log(1):
  case console.log(2):
}
// Only logs 1

如果未找到匹配的 case 子句,则程序将查找可选的 default 子句,如果找到,则将控制权转移到该子句,执行该子句后的语句。如果未找到 default 子句,则程序将继续执行 switch 结尾后的语句。按照惯例,default 子句是最后一个子句,但它不必如此。switch 语句只能有一个 default 子句;多个 default 子句将导致SyntaxError.

中断和贯穿

您可以在 switch 语句的主体中使用break 语句来提前中断,通常在两个 case 子句之间的所有语句都已执行时。执行将继续在 switch 后的第一个语句处继续。

如果省略 break,则执行将继续到下一个 case 子句,即使到 default 子句,无论该子句的表达式的值是否匹配。这种行为称为“贯穿”。

js
const foo = 0;
switch (foo) {
  case -1:
    console.log("negative 1");
    break;
  case 0: // Value of foo matches this criteria; execution starts from here
    console.log(0);
  // Forgotten break! Execution falls through
  case 1: // no break statement in 'case 0:' so this case will run as well
    console.log(1);
    break; // Break encountered; will not continue into 'case 2:'
  case 2:
    console.log(2);
    break;
  default:
    console.log("default");
}
// Logs 0 and 1

在适当的上下文中,其他控制流语句也有效地退出 switch 语句。例如,如果 switch 语句包含在函数中,则return 语句将终止函数主体的执行,因此终止 switch 语句。如果 switch 语句包含在循环中,则continue 语句将停止 switch 语句并跳转到循环的下一轮迭代。

词法作用域

casedefault 子句类似于标签:它们指示控制流可能跳转到的位置。但是,它们本身不会创建词法作用域(也不会自动退出 - 如上所述)。例如

js
const action = "say_hello";
switch (action) {
  case "say_hello":
    const message = "hello";
    console.log(message);
    break;
  case "say_hi":
    const message = "hi";
    console.log(message);
    break;
  default:
    console.log("Empty action received.");
}

此示例将输出错误“Uncaught SyntaxError: Identifier 'message' has already been declared”,因为第一个 const message = 'hello'; 与第二个 const message = 'hi'; 声明冲突,即使它们在各自的 case 子句中。最终,这是因为两个 const 声明都在 switch 主体创建的相同块作用域内。

要修复此问题,无论何时您需要在 case 子句中使用 letconst 声明,请将其包装在块中。

js
const action = "say_hello";
switch (action) {
  case "say_hello": {
    const message = "hello";
    console.log(message);
    break;
  }
  case "say_hi": {
    const message = "hi";
    console.log(message);
    break;
  }
  default: {
    console.log("Empty action received.");
  }
}

此代码现在将在控制台中输出 hello,如预期的那样,没有任何错误。

示例

使用 switch

在以下示例中,如果 expr 评估为 Bananas,则程序会将值与 case case 'Bananas' 匹配并执行关联的语句。遇到 break 时,程序会退出 switch 并执行 switch 后的语句。如果省略 break,则也会执行 case 'Cherries' 的语句。

js
switch (expr) {
  case "Oranges":
    console.log("Oranges are $0.59 a pound.");
    break;
  case "Apples":
    console.log("Apples are $0.32 a pound.");
    break;
  case "Bananas":
    console.log("Bananas are $0.48 a pound.");
    break;
  case "Cherries":
    console.log("Cherries are $3.00 a pound.");
    break;
  case "Mangoes":
  case "Papayas":
    console.log("Mangoes and papayas are $2.79 a pound.");
    break;
  default:
    console.log(`Sorry, we are out of ${expr}.`);
}

console.log("Is there anything else you'd like?");

将 default 子句放在两个 case 子句之间

如果未找到匹配项,则执行将从 default 子句开始,并执行该子句后的所有语句。

js
const foo = 5;
switch (foo) {
  case 2:
    console.log(2);
    break; // it encounters this break so will not continue into 'default:'
  default:
    console.log("default");
  // fall-through
  case 1:
    console.log("1");
}

它在您将 default 放在所有其他 case 子句之前时也能正常工作。

利用贯穿

此方法利用了这样一个事实,即如果在 case 子句下面没有 break,则执行将继续到下一个 case 子句,无论该 case 是否满足条件。

以下是单个操作顺序 case 语句的示例,其中四个不同的值执行完全相同的操作。

js
const Animal = "Giraffe";
switch (Animal) {
  case "Cow":
  case "Giraffe":
  case "Dog":
  case "Pig":
    console.log("This animal is not extinct.");
    break;
  case "Dinosaur":
  default:
    console.log("This animal is extinct.");
}

以下是多个操作顺序 case 子句的示例,其中根据提供的整数,您可以获得不同的输出。这向您展示了它将按照您放置 case 子句的顺序进行遍历,并且它不必是数值顺序。在 JavaScript 中,您甚至可以将字符串的定义混合到这些 case 语句中。

js
const foo = 1;
let output = "Output: ";
switch (foo) {
  case 0:
    output += "So ";
  case 1:
    output += "What ";
    output += "Is ";
  case 2:
    output += "Your ";
  case 3:
    output += "Name";
  case 4:
    output += "?";
    console.log(output);
    break;
  case 5:
    output += "!";
    console.log(output);
    break;
  default:
    console.log("Please pick a number from 0 to 5!");
}

此示例的输出

日志文本
fooNaN 或不是 123450 请选择 0 到 5 之间的数字!
0 输出:那么你的名字是什么?
1 输出:你的名字是什么?
2 输出:你的名字?
3 输出:名字?
4 输出:?
5 输出:!

if...else 链的替代方案

您可能经常发现自己进行一系列if...else 匹配。

js
if ("fetch" in globalThis) {
  // Fetch a resource with fetch
} else if ("XMLHttpRequest" in globalThis) {
  // Fetch a resource with XMLHttpRequest
} else {
  // Fetch a resource with some custom AJAX logic
}

此模式没有进行一系列 === 比较,但您仍然可以将其转换为 switch 结构。

js
switch (true) {
  case "fetch" in globalThis:
    // Fetch a resource with fetch
    break;
  case "XMLHttpRequest" in globalThis:
    // Fetch a resource with XMLHttpRequest
    break;
  default:
    // Fetch a resource with some custom AJAX logic
    break;
}

switch (true) 模式作为 if...else 的替代方案,特别是在您想要利用贯穿行为时非常有用。

js
switch (true) {
  case isSquare(shape):
    console.log("This shape is a square.");
  // Fall-through, since a square is a rectangle as well!
  case isRectangle(shape):
    console.log("This shape is a rectangle.");
  case isQuadrilateral(shape):
    console.log("This shape is a quadrilateral.");
    break;
  case isCircle(shape):
    console.log("This shape is a circle.");
    break;
}

规范

规范
ECMAScript 语言规范
# sec-switch-statement

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅