switch case 语句中的表达式

Posted

技术标签:

【中文标题】switch case 语句中的表达式【英文标题】:Expression inside switch case statement 【发布时间】:2011-03-28 16:32:36 【问题描述】:

我正在尝试创建一个 switch 语句,但我似乎无法使用被评估的表达式(而不是设置的字符串/整数)。我可以使用 if 语句轻松做到这一点,但希望 case 更快。

我正在尝试以下方法

function reward(amount) 
    var $reward = $("#reward");
    switch (amount) 
        case (amount >= 7500 && amount < 10000):
            $reward.text("Play Station 3");
            break;
        case (amount >= 10000 && amount < 15000):
            $reward.text("XBOX 360");
            break;
        case (amount >= 15000):
            $reward.text("iMac");
            break;
        default:
            $reward.text("No reward");
            break;
    

我是否遗漏了一些明显的东西,或者这不可能? Google 在这种情况下并不友好。

任何帮助/指针表示赞赏

M

【问题讨论】:

【参考方案1】:

switch 块不是这样工作的。 case 用于保存单个值,如果它们等于 switch 行上的值。 if-else 声明会很好地为您服务。

这里是关于switch 块的一些信息。

https://developer.mozilla.org/en-US/docs/Web/javascript/Reference/Statements/switch

【讨论】:

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 这个答案还是错的:检查switch(中的specification — ⟨expr1⟩⟨expr2⟩都在switch(⟨ expr1⟩ ) case ⟨expr2⟩ : break; 是表达式。【参考方案2】:

首先,switch 不是这样工作的。您必须为每个case 指定常量,这些常量将与括号中的表达式(在您的情况下为amount)进行比较。这就是switch 的工作方式,句号。

其次,switch不比几个ifs快

第三,在处理 javascript 时,您不应该真正担心微不足道的性能优化。

【讨论】:

好吧,担心性能总是更好。当你必须做一些消耗性能的事情时,它会给你空间。 “您必须为每种情况指定常量” - 不,您不需要。使用常量是使用switch 的最常见方式,但您可以在每个case 中指定任何表达式,带或不带变量或函数调用,值就是要比较的值。 第一段错误:检查specification — switch(中的⟨expr1⟩⟨expr2⟩都有⟨ expr1⟩ ) case ⟨expr2⟩ : break; 是表达式。其他两段更适合作为评论,而不是答案。【参考方案3】:

你总是可以的

switch (true) 
  case (amount >= 7500 && amount < 10000):
    //code
    break;
  case (amount >= 10000 && amount < 15000):
    //code
    break;

  //etc...

之所以有效,是因为true 是一个常量,因此将执行第一个case 语句下的表达式计算结果为真的代码。

我猜这有点“棘手”,但我认为使用它没有任何问题。一个简单的if/else 语句可能会更简洁,您不必担心意外掉线。但无论如何,它就在那里。

【讨论】:

这比丹尼尔斯的“答案”更好。有一个轻微的警告:在导致 case true 的表达式之前的所有表达式也将被评估。小心点。 是的,这是可行的,因为您总是可以将 switch 函数想象成一个跳转表,它只需要匹配并检索一个值。这与 if/else 语句不同,因为所有 if 都需要实际评估。在上面,您要求您的开关评估与匹配。这就是 switch case 更快的原因。 @Vontei - 使用 if/else if/else 结构时,它们不会被 all 评估,仅在特定条件为真之前依次评估它们,即评估 case 表达式时会发生同样的事情。【参考方案4】:

问题是 switch 表达式永远不能等于 case 表达式,因为 case 表达式将计算结果为真或假,但 switch 表达式将是一个数字。

switch 表达式设置为 true 的解决方案不是因为 true 是一个常量,而是因为与 case 表达式相等实际上是可能的。

不必为每个 case 表达式指定常量。

要支持我的回答,请参阅 Douglas Crockford,Javascript The Good Parts (2008),第 12 页:

switch 语句执行多路分支。它将表达式的相等性与所有选定的情况进行比较...。找到完全匹配时,执行匹配的 case 子句的语句... case 子句包含一个或多个 case 表达式。 case 表达式不必是常量。

【讨论】:

可以补充一点,案例块评估是按照源代码的顺序执行的。从上到下。【参考方案5】:

@MooGoo 的switch (true) 会给你一个Weird condition error in jsLint,所以让我们多一些创意,以防出现问题,并且,我认为,增加一点可读性。

所以我们不评估每个casetrue 还是false;我们正在比较 case 的值是否等于我们的 switch 术语。因此,让我们通过在我们的 case 语句中添加一个速记 if 来利用这一点,并如果条件为真则返回我们原来的 switch 项

我还提供了一个现实世界的示例,您希望有两个“默认值”——一个如果您的术语在正方向上超出您的“重要”范围,另一个如果您是反方向。

关键词: case (x &gt; 0 ? x : null):

“如果我的任期 x 大于零,则返回 x 以便 x === x 和我进行案例分支。”

http://jsfiddle.net/rufwork/upGH6/1/

/*global document*/
/*jslint evil:true*/
var x = 10;

switch (x) 
    case (x > 0 ? x : null):
        document.write('ha ha ha!  I fooled switch AND jsLint!  Muhahahahaha!');
        break;
    case 0:
        document.write('zero is nothing.');
        break;
    case -1:
        document.write('low');
        break;
    case -2:
        document.write('lower');
        break;
    case -3: 
        document.write('lowest I care about');
        break;
    default: // anything lower than -3.
        document.write('TOO LOW!!!! (unless you cheated and didn\'t use an int)');

document.write('<br>done.');

快速回复to @Sv443:

请注意default: 开关说:“除非你作弊并且没有使用 int”,并且当你返回 x 时短路需要 x === x

但您的观点是一个有用的提醒,NaN唯一不能应用短路的情况。

也就是说,x 必须 == x 短路 switch 并且,正如 MDN 告诉我们的那样,“NaN, and only NaN, will compare unequal to itself”(双 =)。

这也意味着打开NaN 值(并且 NaN 值)将总是ANY中点击default strong> switch 因为你无法匹配它的值。

这是来自MDN的完整报价:

NaN 将不相等(通过 ==、!=、=== 和 !==)与任何其他值(包括另一个 NaN 值)进行比较。使用Number.isNaN() 或isNaN() 最清楚地确定一个值是否为NaN。或者执行自我比较:NaN,并且只有 NaN,将与自身进行比较。

您可以更改 default 逻辑以检查您拥有的内容:

isNaN(x) ? document.write ('nan') : document.write('TOO LOW!!!! ...)');

或者你甚至可以像 MDN 建议的那样去完全时髦(但请不要;^D):

x !== x ? document.write ('nan') : document.write('TOO LOW!!!! ...)');

【讨论】:

这可能会愚弄 JSLint,但比 JSLint 一开始抱怨的事情更奇怪,更难阅读。如果您有意识地决定在 OP 想要和 MooGoo 解释的情况下使用带有表达式的 switch,那么您就不必担心特定的 JSLint 警告。 @nnnnnn 好吧,我想说switch (true) 完全是反模式,因为MooGoo 本质上是在重新创建if... else if... else。我的主要观点是,这个问题确实让我想到了开关范围的一个可以说是实际的用途——如果你发现自己处于基本上需要两个 (+?) 默认值的switch 范式中。很清楚您可以在case 中使用表达式,返回switch 的参数值,并在该语句处强制调用。但听起来你正在投票给@Daniel 和@FyodorSoikin 的“这不是 switch 的工作原理”的答案,我真的无法争辩 ;) 请注意,这在 x = NaN 时不起作用,因为在内部 JS 可能会进行相等性检查,这对于 NaN 常量是不可能的(它需要使用 Number.isNaN() 进行检查,它不这样做) @Sv443 已在答案更新中解决。在这种情况下,NaN 实际上是证明(嗯,是唯一的例外)规则的例外。 感谢您编辑这个 8 岁的答案 :)【参考方案6】:

你也可以试试我最喜欢的一种结构:

function reward(amount) 
    var $reward = $("#reward");
    $reward.text(
        (amount >= 7500 && amount < 10000) ?    "Play Station 3" :
        (amount >= 10000 && amount < 15000)?    "XBOX 360" :
        (amount >= 15000) ?                     "iMac" :
                                                "No reward"
    );

【讨论】:

如果您反向评估,您是否还需要检查金额是否低于数字? 我想这当然会阻止获得更大奖励的人更快地获得他们;) 嵌套三元运算符常常令人害怕和完全不鼓励。因此最好避免使用它们,除非它们比替代结构更简单、明显且更易于维护。我更喜欢按任何顺序工作的测试。无论您选择如何编写它们,这里的简单性和可维护性都胜过效率。【参考方案7】:

我的 2 美分:

理想情况下 switch (作为原则)应该评估为单个 case 分支,从而实现 O(1) 性能,并且(除了失败的情况)case 语句可以以任何方式重新排序,而无需更改编译器分支策略。

如果使用表达式(假设语言允许),那么理论上,它可以跟随的不仅仅是分支。

编译器(除了那些可以智能地说明开发人员正在尝试做什么的编译器)将无法静态地优化分支策略,从而失去其有效性。

例子:

var x = 6, factors = [];

switch(x)

    case (x%2 == 0): factors.push(2);
    break;

    case (x%3 == 0): factors.push(3);
    break;
    ....
 

在糟糕的代码上期望 cmets

在上面的示例中,编译器没有实用的静态优化方法,因此不会比 if else 获得性能优势。

唯一的部分是,它“可能”对开发人员来说看起来更干净,但实际上可能会在出现其他情况时导致中断。

【讨论】:

【参考方案8】:

好吧,您可以在case 语句中使用表达式,这就是您的开关不是语法错误的原因。但是您必须了解 Case Clause 是使用 ===(严格比较)进行比较的。一旦理解了这一点,该值必须与 switch(expression) 中的表达式值完全匹配,您可以在 js 中货比三家。

函数调用是表达式,所以让我们尝试一下:

function xbox(amount)  return amount >= 10000 && amount < 15000 && amount; 

function reward(amount) 
  var ps3 = function(amount)  return amount >= 7500 && amount < 10000 && amount; 

  function imac(amount)  return amount >= 15000 && amount; 

  var $reward = $("#reward");
  switch (amount) 
    case ps3(amount):
      $reward.text("Play Station 3");
      break;
    case xbox(amount):
      $reward.text("XBOX 360");
      break;
    case imac(amount):
      $reward.text("iMac");
      break;
    default:
      $reward.text("No reward");
      break;
  

reward(8200)// -> Play Station 3
reward(11000)// -> XBOX 360
reward(20000)// -> iMac

如您所见,您既可以使用函数表达式,也可以使用函数定义。没关系。只有 case 子句中的表达式是要计算的表达式。这与您所做的相同,只是您没有返回与金额相同的值,而是返回真值或假值。在我的示例中,如果我的条件为真,我会返回确切的金额,从而触发比较匹配。

这是你的固定代码:

function reward(amount) 
    var $reward = $("#reward");
    switch (amount) 
        case (amount >= 7500 && amount < 10000 && amount):
            $reward.text("Play Station 3");
            break;
        case (amount >= 10000 && amount < 15000 && amount):
            $reward.text("XBOX 360");
            break;
        case (amount >= 15000 && amount):
            $reward.text("iMac");
            break;
        default:
            $reward.text("No reward");
            break;
    

这里是规范:https://tc39.github.io/ecma262/#sec-switch-statement 该链接指向 es2016,因为它比 1999 年的旧 es3 pdf 更容易查找。但它一直都是这样工作的,但这是一个鲜为人知的事实。

然而,我怀疑这是否比 if 语句快。如果你想让你的跑步跑得更快,那么不要触摸 DOM。

【讨论】:

以上是关于switch case 语句中的表达式的主要内容,如果未能解决你的问题,请参考以下文章

switch语句 switch语句的相关知识

switch语句里不需要必须有break吗

Java中String,long,byte类型可以作为switch中的表达式吗?

java中的switch

一些语句(switch case...)

switch结构case语句后的多个语句必须放在花括号中。 这句话对吗?为啥?