大于/小于的 switch 语句

IT技术 javascript jquery comparison switch-statement
2021-01-14 23:23:53

所以我想使用这样的 switch 语句:

switch (scrollLeft) {
  case (<1000):
   //do stuff
   break;
  case (>1000 && <2000):
   //do stuff
   break;
}

现在我知道这些语句 ( <1000) 或 ( >1000 && <2000) 中的任何一个都不起作用(显然出于不同的原因)。我要问的是最有效的方法来做到这一点。我讨厌使用 30 条if语句,所以我宁愿使用 switch 语法。有什么我可以做的吗?

6个回答

当我查看其他答案中的解决方案时,我看到了一些我知道对性能不利的事情。我打算将它们放在评论中,但我认为最好对其进行基准测试并分享结果。你可以自己测试一下以下是我在每个浏览器中以最快速度运行后标准化的结果 (ymmv)。

这是 2021-MAY-05 的结果

测试 铬合金 火狐 歌剧 边缘 勇敢的 节点
1.0 次 15 毫秒 14 毫秒 17 毫秒 17 毫秒 16 毫秒 14 毫秒
如果立即 1.00 1.00 1.00 1.00 1.00 1.00
如果-间接 2.20 1.21 2.06 2.18 2.19 1.93
立即切换 2.07 1.43 1.71 1.71 2.19 1.93
开关量程 3.60 2.00 2.47 2.65 2.88 2.86
开关范围2 2.07 1.36 1.82 1.71 1.94 1.79
开关间接阵列 2.93 1.57 2.53 2.47 2.75 2.50
阵列线性开关 2.73 3.29 2.12 2.12 2.38 2.50
阵列二进制开关 5.80 6.07 5.24 5.24 5.44 5.37

2021 年的测试在 64 位 Windows 10 上执行,使用以下版本:Chrome 90.0.4430.212Firefox 89.0b13Opera 76.0.4017.123Edge 90.0.818.62Brave 1.24.SL8016下运行Node.816 s.1

Apple 没有为 Windows更新Safari,所以它仍然是 5.1.7。我在这次测试中将其更改为 Brave。

以下是 2012 年 9 月 4 日的结果,用于历史比较:

测试 铬合金 火狐 歌剧 微信公众平台 苹果浏览器 节点
1.0 次 37 毫秒 73 毫秒 68 毫秒 184 毫秒 73 毫秒 21 毫秒
如果立即 1.0 1.0 1.0 2.6 1.0 1.0
如果-间接 1.2 1.8 3.3 3.8 2.6 1.0
立即切换 2.0 1.1 2.0 1.0 2.8 1.3
开关量程 38.1 10.6 2.6 7.3 20.9 10.4
开关范围2 31.9 8.3 2.0 4.5 9.5 6.9
开关间接阵列 35.2 9.6 4.2 5.5 10.7 8.6
阵列线性开关 3.6 4.1 4.5 10.0 4.7 2.7
阵列二进制开关 7.8 6.7 9.5 16.0 15.0 4.9

2012 年的测试在 32 位 Windows 7 上执行,使用以下版本:Chrome 21.0.1180.89mFirefox 15.0Opera 12.02MSIE 9.0.8112Safari 5.1.7Node在 Linux 64 位机器上运行,因为 Node for Windows 上的计时器分辨率是 10 毫秒而不是 1 毫秒。

如果立即

这是所有测试环境中最快的方法,除了...鼓声MSIE!(惊喜,惊喜)。

这是实现它的推荐方法。

if (val < 1000) { /*do something */ } else
if (val < 2000) { /*do something */ } else
...
if (val < 30000) { /*do something */ } else

如果-间接

这是switch-indirect-arraybut with if-statements的变体,在所有测试引擎中速度更快。

2021 年,它比最快的测试慢 20-120%(2012 年:0-280%)。Chrome 2021 (2.20) 比 2012 (1.2) 需要更长的时间

values=[
   1000,  2000, ... 30000
];
if (val < values[0]) { /* do something */ } else
if (val < values[1]) { /* do something */ } else
...
if (val < values[29]) { /* do something */ } else

立即切换

当您可以进行计算以获取索引时,这会起作用。

在 2021 年,它比 慢 40-120%(2012 年:0-180%)if-immediate,除了在 MSIE 中它实际上是最快的。

switch (Math.floor(val/1000)) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

开关量程

它很慢,因为引擎必须为每种情况比较两次值。

2021 年,它比最快的测试慢 1-2.6(2012 年:1.6-38)倍。Chrome 从 38 到 3.6 的改进最大,但仍然是最慢的测试引擎。

switch (true) {
  case (0 <= val &&  val < 1000): /* do something */ break;
  case (1000 <= val &&  val < 2000): /* do something */ break;
  ...
  case (29000 <= val &&  val < 30000): /* do something */ break;
}

开关范围2

这是 的变体,switch-range但每个案例只有一个比较,因此速度更快。case 语句的顺序很重要,因为引擎将按照源代码顺序测试每个 case ECMAScript 2020 13.12.9

在 2021 年,它比最快的测试慢 36-107%,但在 2012 年,它慢了 1-31 倍。本次测试中表现最差的依然是Chrome,但从32倍提升到了2倍。

switch (true) {
  case (val < 1000): /* do something */ break;
  case (val < 2000): /* do something */ break;
  ...
  case (val < 30000): /* do something */ break;
}

开关间接阵列

在这个变体中,范围存储在一个数组中。

2021 年,它比最快的测试慢 57-193%(2012 年:3-35 倍)。所有测试引擎的性能都有所提高,虽然 Chrome 仍然是最慢的,但它已从 35 提高到 2.93。

values=[1000,  2000 ... 29000, 30000];

switch(true) {
  case (val < values[0]): /* do something */ break;
  case (val < values[1]): /* do something */ break;
  ...
  case (val < values[29]): /* do something */ break;
}

数组线性搜索

在这个变体中,范围存储在一个数组中。

2021 年,它比最快的测试慢 57-193%(2012 年:3-35 倍)。所有测试引擎的性能都有所提高,虽然 Chrome 仍然是最慢的,但它已从 35 提高到 2.93。

values=[1000,  2000 ... 29000, 30000];

for (sidx=0, slen=values.length; sidx < slen; ++sidx) {
  if (val < values[sidx]) break;
}

switch (sidx) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

阵列二进制开关

这是一种变体,array-linear-switch但带有二分搜索。不幸的是,它比线性搜索慢。不知道是我的实现还是线性搜索更优化了。也可能是键空间太小。

在 2021 年,这慢了 4-5(2012 年:4-16)倍。不要使用.

values=[0, 1000,  2000 ... 29000, 30000];

while(range) {
  range = Math.floor( (smax - smin) / 2 );
  sidx = smin + range;
  if ( val < values[sidx] ) { smax = sidx; } else { smin = sidx; }
}

switch (sidx) {
  case 0: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

结论

如果性能很重要,请使用带有直接值的if-statements 或switch

这就是 stackoverflow 是最佳答案之一的原因。这是一个“永恒”的答案,干得好,感谢 jsfiddle!
2021-03-17 23:23:53
grt信息和解释
2021-03-30 23:23:53
我真的希望我可以+2,这么详细的答案!
2021-04-01 23:23:53
很难看到如此详细且结构整洁的答案。大+1
2021-04-06 23:23:53
Big +1 用于解释此问题的性能方面!
2021-04-07 23:23:53

替代:

var scrollleft = 1000;
switch (true)
{
    case (scrollleft > 1000):
      alert('gt');
      break;
    case (scrollleft <= 1000):
      alert('lt');
      break; 
}

演示:http : //jsfiddle.net/UWYzr/

这是一个更有value的解决方案。+1
2021-03-14 23:23:53
然而,当处理的数据不大并且可能仅应用它的功能时,这种性能损失可以忽略不计,例如验证单个用户输入,然后在这种情况下选择可读性而不是性能。
2021-03-18 23:23:53
这正是我正在寻找的。谢谢!
2021-04-03 23:23:53
虽然编码很优雅,但它会损害性能。在 Chrome 中它比使用if-statements慢近 30 倍在这里看到我的答案
2021-04-06 23:23:53
这不就是一样if(...) else if(...)吗?这确实避免了,if但对我来说听起来并不像一个漂亮的替代品。
2021-04-07 23:23:53
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

只有在你有规律的步骤时才有效......

编辑:由于此解决方案不断获得赞成票,因此我必须建议mofolo 的解决方案更好

@Switz - 请记住,999 < 1000 属于第 0 种情况,但 Math.round(999/1000) 属于第 1 种情况。此外,上面有一个错字,在这种情况下,1 是 >= 1000,而不仅仅是 >1000 .
2021-03-14 23:23:53
mofolo 解决方案的唯一问题是它在 Chrome 中比 IcanDivideBy0 慢了大约 30 倍。下面是我的回答
2021-03-26 23:23:53
Math.round(scrollLeft/1000)顺便用过
2021-04-07 23:23:53

您可以使用条件和与条件对应的功能创建自定义对象

var rules = [{ lowerLimit: 0,    upperLimit: 1000, action: function1 }, 
             { lowerLimit: 1000, upperLimit: 2000, action: function2 }, 
             { lowerLimit: 2000, upperLimit: 3000, action: function3 }];

在这些情况下为您想要执行的操作定义函数(定义函数 1、函数 2 等)

并“评估”规则

function applyRules(scrollLeft)
{
   for(var i=0; i>rules.length; i++)
   {
       var oneRule = rules[i];
       if(scrollLeft > oneRule.lowerLimit && scrollLeft < oneRule.upperLimit)
       {
          oneRule.action();
       }
   }
}

笔记

我讨厌使用 30 个 if 语句

很多时候 if 语句更容易阅读和维护。只有当您有很多条件并且将来很多增长的可能性时,我才会推荐上述内容

更新
正如@Brad 在评论中指出的那样,如果条件是互斥的(一次只能其中一个为真),检查上限就足够了:

if(scrollLeft < oneRule.upperLimit)

如果条件是按升序定义的(例如0 to 1000首先是最低的,然后1000 to 2000是)

我发现这个简洁干净+1
2021-03-15 23:23:53
@Brad,不,这不是我的本意,您说得对,上限就足够了。将其添加为更新...
2021-03-28 23:23:53
action=function1-- 这些不应该是冒号吗?;-) -- 您也可以将其重构为只有一个上限,因为由于消除过程,您不能分为两组——除非这是您的意图(可能有多个操作)。
2021-04-06 23:23:53

你到底在做什么//do stuff

您可以执行以下操作:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc.