switch
switch
语句会对一个表达式求值,并将表达式的值与一系列 case
子句进行匹配,一旦遇到与表达式值相匹配的第一个 case
子句后,将执行该子句后面的语句,直到遇到 break
语句为止。若没有 case
子句与表达式的值匹配,如果没有任何 case
子句与表达式的值匹配,则会跳转至 switch
语句的 default
子句执行。
尝试一下
语法
switch (expression) {
case caseExpression1:
statements
case caseExpression2:
statements
// …
case caseExpressionN:
statements
default:
statements
}
expression
-
结果将与每个
case
子句进行匹配的一个表达式。 case caseExpressionN
可选-
case
子句用于与expression
进行匹配。如果expression
的值与任何caseExpressionN
的值匹配,则从该case
子句之后的第一个语句开始执行,直到遇到switch
语句结束或首个break
语句为止。 default
可选-
default
子句;如果存在,则当expression
的值与任何case
句都不匹配时,会执行此子句。一个switch
语句只能有一个default
子句。
描述
switch
语句首先对其表达式进行求值。然后,它会查找第一个case
子句,该子句的表达式求值结果与输入表达式的结果相同(通过严格相等比较) ,并将控制权转移到该子句,执行该子句之后的所有语句。
仅当必要时才会对子句表达式进行求值——如果已经找到了匹配项,则后续的 case
子句表达式将不再进行求值,即使它们可能会因跳出和穿透机制而被执行到。
switch (undefined) {
case console.log(1):
case console.log(2):
}
// 仅输出 1
若找不到匹配的 case
子句,程序会查找可选的 default
子句,如果找到,则将控制权转移到该子句,并执行该子句后面的语句。如果找不到 default
子句,程序将继续执行 switch
结束后的语句。按照惯例,default
子句通常位于最后一个位置,但实际上并不强制要求如此。一个 switch
语句只能有一个 default
子句;多个 default
子句会导致 SyntaxError
错误。
跳出和穿透
你可以在 switch
语句体内部使用 break
语句提前跳出,通常是在执行完两个 case
子句之间的所有语句后。执行会从 switch
语句后的第一条语句继续进行。
如果省略了 break
语句,程序执行将会继续流向下一个 case
子句,甚至到达 default
子句,而不论该子句中的表达式值是否匹配。这种行为被称为“穿透(fall-through)”。
const foo = 0;
switch (foo) {
case -1:
console.log("负 1");
break;
case 0: // foo 的值匹配这个条件;执行从这里开始
console.log(0);
// 忘记了 break!执行穿透
case 1: // 'case 0:' 中没有 break 语句,所以这个 case 也会执行
console.log(1);
break; // 遇到 break,不会继续到 'case 2:'
case 2:
console.log(2);
break;
default:
console.log("default");
}
// 输出 0 和 1
在合适的上下文中,其他控制流语句同样具有跳出 switch
语句的效果。例如,如果 switch
语句嵌套在一个函数内部,那么 return
语句将结束函数体的执行,因此也会结束 switch
语句的执行。如果 switch
语句位于循环体内,那么 continue
语句会停止 switch
语句的执行,并跳转到循环体的下一次迭代。
词法作用域
case
和 default
子句类似于标记语句:它们指示了控制流可能跳转到的位置。然而,它们本身并不创建词法作用域(也不会自动跳出——如上所述所示)。例如:
const action = "说你好";
switch (action) {
case "说你好":
const message = "你好";
console.log(message);
break;
case "说嘿":
const message = "嘿";
console.log(message);
break;
default:
console.log("action 的声明为空。");
}
此示例将抛出错误“Uncaught SyntaxError: Identifier 'message' has already been declared”,因为第一个 const message = '你好';
声明与第二个 const message = '嘿';
声明发生了冲突,即使它们分别位于各自的 case 子句内。从根本上说,这是因为两个 const
声明都在同一个由 switch
语句体所创建的块作用域内。
要修复这个问题,当你需要在 case
子句中使用 let
或 const
声明时,请将其包裹在一个代码块中。
const action = "说你好";
switch (action) {
case "说你好": {
const message = "你好";
console.log(message);
break;
}
case "说嘿": {
const message = "嘿";
console.log(message);
break;
}
default: {
console.log("action 的声明为空。");
}
}
现在,这段代码将正常在控制台输出 你好
,不会再出现任何错误。
示例
使用 switch
在以下示例中,如果 expr
的计算结果为 香蕉
,则程序会将其值与 case '香蕉'
子句进行匹配,并执行相应的语句。当遇到 break
关键字时,程序会跳出 switch
语句,并执行紧随其后的 switch
语句。如果省略了 break
,则 case '樱桃'
的语句也会被执行。
switch (expr) {
case "橙子":
console.log("橙子每磅 $0.59 美元。");
break;
case "苹果":
console.log("苹果每磅 $0.32 美元。");
break;
case "香蕉":
console.log("香蕉每磅 $0.48 美元。");
break;
case "樱桃":
console.log("樱桃每磅 $3.00 美元。");
break;
case "芒果":
case "木瓜":
console.log("芒果和木瓜每磅 $2.79 美元。");
break;
default:
console.log(`抱歉,我们没有 ${expr} 了。`);
}
console.log("你还需要什么吗?");
将 default 子句置于两个 case 子句之间
如果没有找到匹配项,将从 default
字句开始执行,并执行该子句之后的所有语句。
const foo = 5;
switch (foo) {
case 2:
console.log(2);
break; // 由于遇到了 break,因此不会继续执行 'default:'
default:
console.log("default");
// 穿透
case 1:
console.log("1");
}
即使将 default
子句放在所有其他 case
子句之前也可以实现相同的效果。
利用穿透特性
这种方法利用了这样一个事实,如果在某个 case
子句下方没有 break
语句,那么无论该 case
子句是否满足条件,程序都会继续执行下一个 case
子句。
以下是一个单操作连续 case
语句的示例,其中四个不同的值执行完全相同的操作。
const Animal = "长颈鹿";
switch (Animal) {
case "奶牛":
case "长颈鹿":
case "狗":
case "猪":
console.log("这类动物没有灭绝。");
break;
case "恐龙":
default:
console.log("这类动物已经灭绝。");
}
以下是一个涉及多个操作的连续 case
子句示例,根据提供的整数值,可以获得不同的输出结果。这表明 case
子句将会按照你编写时的顺序依次执行,而不必按照数值的大小顺序。在 JavaScript 中,这些 case
语句中甚至还可以混入字符串类型的定义。
const foo = 1;
let output = "输出:";
switch (foo) {
case 0:
output += "所以";
case 1:
output += "你的";
output += "名字";
case 2:
output += "叫";
case 3:
output += "什么";
case 4:
output += "?";
console.log(output);
break;
case 5:
output += "!";
console.log(output);
break;
default:
console.log("请从 0 到 5 中选择一个数字!");
}
此示例的输出结果:
值 | 输出文本 |
---|---|
foo 是 NaN 或不等于 1 、2 、3 、4 、5 或 0 |
请从 0 到 5 中选择一个数字! |
0 |
输出:所以你的名字叫什么? |
1 |
输出:你的名字叫什么? |
2 |
输出:叫什么? |
3 |
输出:什么? |
4 |
输出:? |
5 |
输出:! |
一种替代 if...else 链的方法
你可能经常会遇到需要使用一系列 if...else
条件判断的情况。
if ("fetch" in globalThis) {
// 使用 fetch 获取资源。
} else if ("XMLHttpRequest" in globalThis) {
// 使用 XMLHttpRequest 获取资源。
} else {
// 使用自定义 AJAX 逻辑获取资源
}
这种模式并非在执行一系列 ===
等值比较操作,但仍然可以将其转换为 switch
结构来实现。
switch (true) {
case "fetch" in globalThis:
// 使用 fetch 获取资源。
break;
case "XMLHttpRequest" in globalThis:
// 使用 XMLHttpRequest 获取资源。
break;
default:
// 使用自定义 AJAX 逻辑获取资源
break;
}
switch (true)
模式作为 if...else
结构的一种替代方案,在希望利用穿透行为时特别有用。
switch (true) {
case isSquare(shape):
console.log("该形状是一个正方形。");
// 失败,因为正方形也是矩形的一种!
case isRectangle(shape):
console.log("该形状是一个矩形。");
case isQuadrilateral(shape):
console.log("该形状是一个四边形。");
break;
case isCircle(shape):
console.log("该形状是一个圆形。");
break;
}
规范
Specification |
---|
ECMAScript Language Specification # sec-switch-statement |
浏览器兼容性
BCD tables only load in the browser