在《JavaScript: The Good Parts》里,作者并不赞成 switch
语句的使用(主要是因为 fall-through 的情况很容易造成错误)。然而在实际的代码里,还是有不少地方可以看到 switch 的使用。目的各不相同,有不少可以借鉴的地方。
默认值设置
React 的 Scheduler 中,有这样一段代码:
switch (priorityLevel) {
case ImmediatePriority:
case UserBlockingPriority:
case NormalPriority:
case LowPriority:
case IdlePriority:
break;
default:
priorityLevel = NormalPriority;
}
不失为设置默认值的一种写法,看上去比使用 if 来得更明确一些:
if (
priorityLevel !== ImmediatePriority &&
priorityLevel !== UserBlockingPriority &&
priorityLevel !== NormalPriority &&
priorityLevel !== LowPriority &&
priorityLevel !== IdlePriority
) {
priorityLevel = NormalPriority;
}
防止代码篡改的判定
上面的需求,也很容易写成下面这种数组的方案:
const allowedValues = [
ImmediatePriority,
UserBlockingPriority,
NormalPriority,
LowPriority,
IdlePriority,
];
const isNot = value => comparedTo => value !== comparedTo;
if (allowedValues.every(isNot(priorityLevel))) {
priorityLevel = NormalPriority;
}
然而,这样的代码方式,可能存在被入侵的危险。不论是上面例子中的 every
函数,还是用 Array.prototype
上的任意函数,都有被篡改的可能性。如果其他地方的代码修改了 Array.prototype.every
的行为,让这里的返回值发生了变化,那么代码最终就会产生意料之外的行为。
在 Scheduler 中当然不需要考虑这个问题,但是在其他的应用场景下,这可能是不得不考虑的问题。举例来说,如果一个 Web 应用允许第三方脚本的运行,同时自身有对数据进行白名单检查的需求,那么就只能使用 switch 硬编码所有的情况,而不能使用数组或者对象,否则第三方的脚本有可能对最终的行为做篡改。
Microsoft Teams 的代码里,就有类似的应用场景(见 extracted/lib/renderer/preload_sandbox.js):
const isChannelAllowed = (channel) => {
// ...
let isAllowed = false;
// IMPORTANT - the allowList must be a hardcorded switch statement.
// Array and object methods can be overridden and forced to return true.
switch (channel) {
case xxx:
// ...
case zzz:
isAllowed = true;
break;
default:
isAllowed = false;
break;
}
}