JavaScript的switch/case语句

原文:The Switch/Case Statement in JavaScript

switch语句就像多个叠加的if语句,可以在不同条件执行不同的代码。本文展示了如何使用switch/case语句。

switch语句计算一个表达式,计算结果等于某个case的值时,就会执行这个case的语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const hero = 'Batman';
let sidekick;

switch (hero) {
case 'Batman':
sidekick = 'Robin';
break;
case 'Aquaman':
sidekick = 'Aqualad';
break;
case 'Superman':
sidekick = 'Jimmy Olsen';
break;
default:
throw new Error('Unknown hero');
}

sidekick; // 'Robin'

一定别忘了在语句块后加break!如果在case语句块最后没有break,JavaScript会继续执行下一个case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const hero = 'Batman';
let sidekick;

switch (hero) {
case 'Batman':
sidekick = 'Robin';
// 如果没有break,会执行下一个case语句块
// break;
case 'Aquaman':
sidekick = 'Aqualad';
break;
case 'Superman':
sidekick = 'Jimmy Olsen';
break;
default:
throw new Error('Unknown hero');
}

// 'Batman'和'Aquaman'语句块都会执行
// 得到的sidekick是错误的
sidekick; // 'Aqualad'

这个语言特点也有一些妙用,可以多个case条件执行同一个语句块。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const sidekick = 'Nightwing';
let hero;

switch (sidekick) {
// 'Robin'和'Nightwing'的case属于是路过case语句。
// 它们会执行和'Bluebird'的case相同的语句块。
case 'Robin':
case 'Nightwing':
case 'Bluebird':
hero = 'Batman';
break;
case 'Aqualad':
case 'Tempest':
hero = 'Aquaman';
break;
default:
throw new Error('Unknown sidekick');
}

hero; // 'Batman'

相等检查

switch语句先计算给定表达式的值,然后用严格相等来比较每个case的值。以下if语句在功能上与第一个例子是等效的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const hero = 'Batman';
let sidekick;

if (hero === 'Batman') {
sidekick = 'Robin';
} else if (hero === 'Aquaman') {
sidekick = 'Aqualad';
} else if (hero === 'Superman') {
sidekick = 'Jimmy Olsen';
} else {
throw new Error('Unknown hero');
}

sidekick; // 'Robin'

因为switch语句使用严格相等,要比较对象时要注意做好类型转换,例如日期MongoDB的ObjectId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const date = new Date('2020/07/04');
let holiday;

const goodFriday = new Date('2020/04/10');
const independenceDay = new Date('2020/07/04');
const christmas = new Date('2020/12/25');

// 只有两个日期指向同一个对象,它们才是严格相等。
date === independenceDay; // false

// date是一个对象,因此要用getTime()转换为数值
// 否则所有的case都不会执行
switch (date.getTime()) {
case goodFriday.getTime():
holiday = 'Good Friday';
break;
case independenceDay.getTime():
holiday = 'Independence Day';
break;
case christmas.getTime():
holiday = 'Christmas';
break;
}

holiday; // 'Independence Day'

替代方案

除了路过式的case语句,其他情况都可以用if语句来替代switch/case。另一种替代方案是定义一个对象或Map,以case条件作为key,包含对应的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const hero = 'Batman';
let sidekick;

const obj = {
'Batman': () => { sidekick = 'Robin'; },
'Aquaman': () => { sidekick = 'Aqualad'; },
'Superman': () => { sidekick = 'Jimmy Olsen'; }
};

// 要为对象使用hasOwnProperty()
// 否则constructor也会是一个有效的hero
if (obj.hasOwnProperty(hero)) {
obj[hero]();
}

sidekick; // 'Robin'

用对象实现的条件分支,优势是能以编程方式构建对象。如果switch语句有点重复冗长,可以考虑用for循环,构建一个条件分支的对象。

建议

switch语句有许多不直观之处,例如意外的跳到下一个case语句。ESLint有一个no-fallthrough规则,可以在lint层规避这个问题。然而,大部分情况下if/else语句或对象条件都是更好的选择,因为switch比较少见,大多数人对switch的语义不敏感。