共计 2920 个字符,预计需要花费 8 分钟才能阅读完成。
JavaScript 的 switch 有四样写法,你晓得么?不论你晓得不晓得,反正我是不晓得。
我所晓得的 JavaScript 的 switch 语句只有一种写法。但要说到对分支的解决,写法可就多了去了。if 分支写法能够算一种,switch 分支写法能够算第二种,第三种是应用策略模式,如果要把条件运算符也算上的话,嗯,刚好四种。
不过本文的配角是 switch。大家都理解 switch 的写法一般来说是 switch 变量或表达式,case 常量。嗯,比如说,一个百分制问题,90 及 90 分以上算优良,80 及以上 90 以下算良好,60 及以上 80 以下算合格,60 以下为不合格,用 switch 大略会这么写:
function calcGrade(score) {
const line = score / 10 | 0;
switch (line) {
case 10: case 9:
return "优良";
case 8:
return "良好";
case 7: case 6:
return "合格";
default:
return "不合格";
}
}
代码中
score / 10 | 0
和Math.floor(score / 10)
是一样的成果,就是除以 10 取商的整数局部。
这段 switch 用得中规中矩,用取整的方法来防止应用一长串 if … else 分支也算是取了巧。
然而当初规定改了,将合格和良好的分隔点从 80 分降到 75 分,该怎么办?
按下面取整的方法仍然能够,不过这次除数不再是 10,而是 5。相应地,case 也多了很多:
- 18、19、20 是优良
- 15、16、17 是良好
- 12、13、14 是合格
- 剩下的是不合格
写 9 个 case,真不如用 if … else 算了。
是吗?其实用 switch 也有简略一些的写法:
function calcGrade(score) {switch (true) {
case score >= 90:
return "优良";
case score >= 75:
return "良好";
case score >= 60:
return "合格";
default:
return "不合格";
}
}
是不是感觉有些奇怪?这齐全不是习惯了的 switch 表达式 case 常量,而是正好相同,switch 常量 case 表达式!如果你拿这段程序去跑一下,会发现一点问题都没有。因为——switch 和 case 是按 ===
来匹配的,它并不在乎是表达式还是常量,或者说,switch 和 case 前面都能够接表达式!
是的,表达式!
所以下面那个示例中,把 switch(true)
改成 switch(2 > 1)
也是一样的成果。
好啦,脑洞已开。switch 到底有多少种写法曾经不重要了。接下来要钻研的是 switch 的变种 ——
看到 C# 有 switch 表达式,眼馋,能实现吗?
不必眼馋,JavaScript 里所有都能够是表达式 …… 如果不是,用 IIFE 封装一个就是了
function calcGrade(score) {
return (value => {switch (true) {
case value >= 90:
return "优良";
case value >= 75:
return "良好";
case value >= 60:
return "合格";
default:
return "不合格";
}
})(score);
}
留神这里把 score
作为 IIFE 的参数,是因为在理论应用中,可能须要传入的是一个表达式。这种状况下应该提前求值,而且只求一次(防止替在的副作用)。
不过这样的封装显然没什么意义,如果真要这样封装,不如封成策略:
function calcGrade(score) {return ((value, rules) => rules.find(({t}) => t(value)).v)(
score,
[{ t: n => n >= 90, v: "优良"},
{t: n => n >= 75, v: "良好"},
{t: n => n >= 60, v: "合格"},
{t: () => true, v: "不合格" },
]
);
}
每项策略都是一个含有 tester (t
) 和值 (v
) 的对象。tester 是一个判断函数,传入须要判断的值,也就是 switch (表达式)
这里表达式,而这个表达式也是提前求值之后作为 IIFE 的参数传入的。利用策略的过程简略粗犷,就是找到第一个符合条件的策略,把它的值取出来。
当然这样用策略有点大材小用。真正须要用策略的状况,策略中通常不是一个值,而是一个行为,也就函数。
咱们晓得在 switch 语句中,各个 case 之间是在同一个作用域内,所以不能在两个 case 语句中申明同一个局部变量。尽管用 {}
包裹能够解决这些问题,但代码看起来不怎么难看,特地是还要留神不要忘了 break
。如果用策略的话,看起来可能会悦目一眼,也不必放心 break 的问题:
这里为了演示,在策略行为中将先输入问题,再返回等级。
function calcGrade(score) {return ((value, rules) => rules.find(({t}) => t(value)).fn(value))(
score,
[
{
t: n => n >= 90,
fn: score => {
const grade = "优良";
console.log(grade, score);
return grade;
}
},
{
t: n => n >= 75,
fn: score => {
const grade = "良好";
console.log(grade, score);
return grade;
}
},
{
t: n => n >= 60,
fn: score => {
const grade = "合格";
console.log(grade, score);
return grade;
}
},
{t: () => true,
fn: score => {
const grade = "不合格";
console.log(grade, score);
return grade;
}
},
]
);
}
代码的确有点长,因为有策略行为逻辑在外面。如果真的是要当作 switch 表达式 来用的话,策略局部应该是一个表达式,不会太长的。下面的代码中,策略行为类似,能够封装成一个函数,这样就能写成表达式的模式了:
function calcGrade(score) {const printGrade = (grade, score) => {console.log(grade, score);
return grade;
};
return ((value, rules) => rules.find(({t}) => t(value)).fn(value))(
score,
[{ t: n => n >= 90, fn: score => printGrade("优良", score) },
{t: n => n >= 75, fn: score => printGrade("良好", score) },
{t: n => n >= 60, fn: score => printGrade("合格", score) },
{t: () => true, fn: score => printGrade("不合格", score) },
]
);
}
当初看起来是不是像样了?
下面的代码形式各异,干的事件都差不多,也没有谁优谁劣的比拟。看得悦目怎么都优雅,看不顺眼怎么都不受宠。在不同的状况下,选用适合的做法就好。下面的代码应用的 find()
来查找策略,如果改用 filter()
,那又会是另一番现象了 ~(~~▽~)~
。