switch

让我们简单探索一下switch语句,某种if..else if..else..语句链的语法缩写。

  1. switch (a) {
  2. case 2:
  3. // 做一些事
  4. break;
  5. case 42:
  6. // 做另一些事
  7. break;
  8. default:
  9. // 这里是后备操作
  10. }

如你所见,它对a求值一次,然后将结果值与每个case表达式进行匹配(这里只是一些简单的值表达式)。如果找到一个匹配,就会开始执行那个匹配的case,它将会持续执行直到遇到一个break或者遇到switch块儿的末尾。

这些可能不会令你吃惊,但是关于switch,有几个你以前可能从没注意过的奇怪的地方。

首先,在表达式a和每一个case表达式之间的匹配与===算法(见第四章)是相同的。switch经常在case语句中使用绝对值,就像上面展示的,因此严格匹配是恰当的。

然而,你也许希望允许宽松等价(也就是==,见第四章),而这么做你需要“黑”一下switch语句:

  1. var a = "42";
  2. switch (true) {
  3. case a == 10:
  4. console.log( "10 or '10'" );
  5. break;
  6. case a == 42:
  7. console.log( "42 or '42'" );
  8. break;
  9. default:
  10. // 永远不会运行到这里
  11. }
  12. // 42 or '42'

这可以工作是因为case子句可以拥有任何表达式(不仅是简单值),这意味着它将用这个表达式的结果与测试表达式(true)进行严格匹配。因为这里a == 42的结果为true,所以匹配成功。

尽管==switch的匹配本身依然是严格的,在这里是truetrue之间。如果case表达式得出truthy的结果而不是严格的true,它就不会工作。例如如果在你的表达式中使用||&&这样的“逻辑操作符”,这就可能咬到你:

  1. var a = "hello world";
  2. var b = 10;
  3. switch (true) {
  4. case (a || b == 10):
  5. // 永远不会运行到这里
  6. break;
  7. default:
  8. console.log( "Oops" );
  9. }
  10. // Oops

因为(a || b == 10)的结果是"hello world"而不是true,所以严格匹配失败了。这种情况下,修改的方法是强制表达式明确成为一个truefalse,比如case !!(a || b == 10):(见第四章)。

最后,default子句是可选的,而且它不一定非要位于末尾(虽然那是一种强烈的惯例)。即使是在default子句中,是否遇到break的规则也是一样的:

  1. var a = 10;
  2. switch (a) {
  3. case 1:
  4. case 2:
  5. // 永远不会运行到这里
  6. default:
  7. console.log( "default" );
  8. case 3:
  9. console.log( "3" );
  10. break;
  11. case 4:
  12. console.log( "4" );
  13. }
  14. // default
  15. // 3

注意: 就像我们前面讨论的打标签的breakcase子句内部的break也可以被打标签。

这段代码的处理方式是,它首先通过所有的case子句,没有找到匹配,然后它回到default子句开始执行。因为这里没有break,它会继续走进已经被跳过的块儿case 3,在遇到那个break后才会停止。

虽然这种有些迂回的逻辑在JavaScript中是明显可能的,但是它几乎不可能制造出合理或易懂的代码。要对你自己是否想要创建这种环状的逻辑流程保持怀疑,如果你真的想要这么做,确保你留下了大量的代码注释来解释你要做什么!