如何缩短我的条件陈述


154

我有一个很长的条件语句,如下所示:

if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
    // do something.
}

我想知道是否可以将此表达/陈述重构为更简洁的形式。

关于如何实现这一点的任何想法?


23
您可以将它们放在数组中并使用in
jeremy


现在,只要有人可以检查哪个是最快的就可以了
Muhammad Umer

3
这可能会让所有人震惊,但是OP的速度无疑是赢家!!!!可能导致浏览器为此进行了很多优化。结果:(1)如果使用||。(2)switch陈述。(3)正则表达式。(4)~jsperf.com/if-statements-test-techsin
Muhammad Umer

3
您也可能以错误的方式来处理此问题。在这种情况下,这四种类型有一些共同点。它是什么?如果我们将其带入一个更极端的情况,如果我们需要再添加10个类型以匹配此条件,该怎么办。还是100?如果还有更多,您可能不会考虑使用此解决方案,或者不建议使用其他任何解决方案。您会看到类似这样的大if语句,并认为这是一种代码味道,这是一个好兆头。使其更简洁的最佳方法是编写if(test.your_common_condition)。在这种情况下更容易理解,并且可扩展。
gmacdougall

Answers:


241

将您的值放入数组,然后检查您的项目是否在数组中:

if ([1, 2, 3, 4].includes(test.type)) {
    // Do something
}

如果您支持的浏览器没有该Array#includes方法,则可以使用此polyfill


~波浪号快捷方式的简短说明:

更新:由于我们现在有了该includes方法,因此~不再需要使用该hack。只是将其保留在此处,以供有兴趣了解它的工作原理和/或在他人的代码中遇到过它的人使用。

除了检查结果是否indexOf为之外>= 0,还有一个不错的捷径:

if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
    // Do something
}

这是小提琴:http : //jsfiddle.net/HYJvK/

这是如何运作的?如果在数组中找到一个项目,则indexOf返回其索引。如果找不到该项目,它将返回-1。无需赘述,~按位NOT运算符,该运算符0仅返回-1

我喜欢使用~快捷方式,因为它比对返回值进行比较简洁。我希望JavaScript具有一个in_array直接返回布尔值的函数(类似于PHP),但这只是一厢情愿(Update:现在可以了。称为includes。)。请注意,jQuery inArray共享了PHP的方法签名时,实际上模仿了本机indexOf功能(在不同情况下,如果索引才是您真正想要的,该功能将很有用)。

重要说明:使用波浪号快捷方式似乎引起了争议,因为一些人强烈认为代码不够清晰,应不惜一切代价避免使用(请参阅此答案的注释)。如果您有共同的想法,则应该坚持.indexOf(...) >= 0解决。


稍长一点的解释:

JavaScript中的整数是带符号的,这意味着最左边的位被保留为符号位;一个标志,指示数字是正数还是负数,1负数表示。

以下是一些32位二进制格式的正数示例:

1 :    00000000000000000000000000000001
2 :    00000000000000000000000000000010
3 :    00000000000000000000000000000011
15:    00000000000000000000000000001111

现在,这些数字相同,但为负数:

-1 :   11111111111111111111111111111111
-2 :   11111111111111111111111111111110
-3 :   11111111111111111111111111111101
-15:   11111111111111111111111111110001

为什么对负数使用这种怪异的组合?简单。负数就是正数+ 1的倒数;将负数加上正数总是应该产生的0

为了理解这一点,让我们做一些简单的二进制算术。

这是我们将添加-1到的方法+1

   00000000000000000000000000000001      +1
+  11111111111111111111111111111111      -1
-------------------------------------------
=  00000000000000000000000000000000       0

这是我们将添加-15到的方式+15

   00000000000000000000000000001111      +15
+  11111111111111111111111111110001      -15
--------------------------------------------
=  00000000000000000000000000000000        0

我们如何获得这些结果?通过定期加法,这就是我们在学校所教的方式:您从最右边的列开始,然后将所有行加起来。如果总和大于最大的一位数字(十进制为9,而二进制为1),则将剩余的数字带入下一列。

现在,您会注意到,在将负数添加到其正数时,不是all 0的最右边的列将始终具有两个1s,将它们加在一起将得到2。两个be的二进制表示形式10,我们将携带1到下一列,然后将0结果放在第一列。左侧的所有其他列只有一个带有a的行1,因此1从上一列开始的结转将再次加到2,然后将继续结转...此过程会重复进行,直到我们到达最左侧的列为止。在1被结转无处可去,所以它溢出和丢失,我们就只剩下0一切都跨越。

该系统称为2的补码。您可以在此处了解更多信息:

2的有符号整数的补码表示形式


现在,以2的补码结束的崩溃过程已经结束,您将注意到,这-1是唯一二进制表示为1的数字。

使用~按位NOT运算符,可以反转给定数字中的所有位。0从所有位取反的唯一方法是,如果我们从整体开始1

因此,所有这一切都是漫长的说法,~n只有0nis 时才会返回-1


59
虽然使用按位运算符确定确实很性感,但它真的比!== -1以任何可以想象的方式好吗?显式布尔逻辑是否比隐式使用零错误更合适?
Phil

21
很好,但我不喜欢它。乍一看尚不清楚代码在做什么,这使其无法维护。我更喜欢“ Yuriy Galanter”的回答。
乔恩·瑞阿2013年

65
-1新的程序员看到这样的答案,认为这是编码的冷静和接受的方式,然后在5年内我要保持自己的代码和撕裂了我的头发
丹尼Pflughoeft - BlueRaja

23
这种习语在C#,Java或Python等语言中绝对不常见,这是我的专业领域。我只是问了这里的一些本地Javascript专家,他们中没有一个人以前见过它。因此它显然不像您所声称的那样普遍。 应始终避免使用这种方法,而应使用更清晰,更常见的方法!= -1
BlueRaja-Danny Pflughoeft13年

12
-1是由于不必要的位摆弄,而该语言最初并没有真正说明很多位表示。另外,这个答案的大部分是在解释位戏。如果您必须编写20段来解释该hack,那么它真的可以节省时间吗?
蓬松的

242

您可以通过以下方式使用switch语句:

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}

9
它与if基本相同,数组方法的索引要好得多
NimChimpsky 2013年

6
@kojiro令人遗憾的是,正确的答案是这个,但是不可能引起注意,而不是令人敬畏的bitwhise-array技巧。
Manu343726

1
我知道这个答案必须在这里,但我必须向下滚动到底部才能找到它。这正是switch语句旨在用于并延续到许多其他语言的目的。我发现很多人都不知道switch语句中的“ fall through”方法。
吉米·约翰逊

3
该解决方案在Firefox和Safari上是最快的,||在Chrome上是第二快(仅次于原始版本)。参见jsperf.com/if-statements-test-techsin
pabouk

3
我认为如果您有许多条件,用一种方法编写该方法将很糟糕...如果在某些条件下可以,但是对于10个以上的条件,我会选择使数组保持代码整洁的方法。
yu_ominae 2013年

63

使用科学:您应该执行idfah所说的,以最快的速度进行操作,同时保持代码简短:

这比~方法更快

var x = test.type;
if (x == 'itema' ||
    x == 'itemb' ||
    x == 'itemc' ||
    x == 'itemd') {
    //do something
}

http://jsperf.com/if-statements-test-techsin 在此处输入图片说明 (顶部设置:Chrome,底部设置:Firefox)

结论:

如果可能性是少数,你知道,某些是更有可能比你得到最大的性能出去发生if ||switch fall throughif(obj[keyval])

如果可能性很多,并且其中任何一个都可能是发生最多的一种,换句话说,您不可能知道哪个可能性最大,而不是通过对象查找获得最大的性能if(obj[keyval])regex如果合适的话。

http://jsperf.com/if-statements-test-techsin/12

如果有新的事情我会更新。


2
+1个好帖子!如果我理解正确,那switch case是最快的方法吗?
user1477388

1
在Firefox是的,在它的铬if ( ...||...||...)...
穆罕默德乌默尔

8
这是,如果你真的在此输入多循环更快,但它是多少,如果你有一个循环具有非常大的N(的“itemX”串号)慢。我破解了此代码生成器,您可以使用它来验证(或驳斥)。obj["itemX"]如果n大,则非常快。基本上,快速的操作取决于上下文。玩得开心。
kojiro

3
所以这是最快的方法,但这有关系吗?
congusbongus

1
@Mich不要为了速度而牺牲代码的优雅。这就是许多人会对您说的话。最后,仅需使用常识即可。
Andre Figueiredo

32

如果要与字符串进行比较并且存在某种模式,请考虑使用正则表达式。

否则,我怀疑尝试缩短它只会混淆您的代码。考虑简单地包装线条使其漂亮。

if (test.type == 'itema' ||
    test.type == 'itemb' ||
    test.type == 'itemc' ||
    test.type == 'itemd') {
    do something.
}

4
这个答案是速度方面的赢家 jsperf.com/if-statements-test-techsin
Muhammad Umer

1
当项目进入维护模式时(使用诸如的规则(test.type == 'itemf' && foo.mode == 'detailed')),这也是最容易扩展的方法
Izkata 2013年

16
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) {  }

将对象用作关联数组是很常见的事情,但是由于JavaScript没有本机集,因此您也可以将对象用作廉价集。


它比FlyingCat试图缩短的常规if语句短吗?
dcarson

1
if如果删除所有空格,@ dcarson OP的语句条件将占用78个字符。矿井需要54如果你把它写这样的:test.type in {"itema":1,"itemb":1,"itemc":1,"itemd":1}。从根本上说,他为每个附加键每使用两个地雷就使用四个字符。
2013年

1
但是您可以:if(possibilities [test.type])并保存整个2个字符!:)
dc5

15
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

或如果项目不是那么统一,则:

if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }

9
“有些人遇到问题时会想,'我知道,我会使用正则表达式。' 现在他们有两个问题。” -Jamie Zawinski,1997年
Moshe Katz

5
@MosheKatz虽然我可以理解人们喜欢对这个报价感到厌烦-而且人们当然确实对完全不合适的东西使用正则表达式,但这不是其中之一。在OP提供的情况下,这不仅符合条件,而且效果很好。正则表达式并不是天生就邪恶的,匹配具有良好定义的参数的字符串就是它的目的。
Thor84no 2013年

3
@ Thor84no通常,我会认为发问者实际上并没有尝试与第一种情况那样的人为例子进行匹配,并且现实世界中的匹配并非如此简单,在这种情况下,我认为RegEx不会进行是正确的方法 换句话说,如果RegEx只是一个由竖线分隔的选项列表,那么它的可读性不会比其他任何建议高,而且效率可能会大大降低。
Moshe Katz

10

很好的答案,但是您可以通过将其中之一包装在函数中来提高代码的可读性。

如果if语句很复杂,那么当您(或其他人)在几年后阅读代码时,将通过扫描找到该部分以了解正在发生的事情。具有这种业务逻辑级别的语句将使您在制定测试内容时绊倒几秒钟。像这样的代码,将允许您继续扫描。

if(CheckIfBusinessRuleIsTrue())
{
    //Do Something
}

function CheckIfBusinessRuleIsTrue() 
{
    return (the best solution from previous posts here);
}

明确命名您的函数,以便立即清楚您要测试的内容,并且使代码更易于扫描和理解。


1
我在这里看到的最佳答案。确实,我看到人们并不关心好的设计原则。只是想快速修复问题而忘了改进代码,以便将来可以维护系统!
2013年

像评论一样// CheckIfBusinessRuleIsTrue怎么样?
daniel1426 2014年

4

您可以将所有答案放入Javascript集,然后仅调用.contains()集。

您仍然必须声明所有内容,但是内联调用会更短。

就像是:

var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}

4
这似乎是完成OP试图完成的工作的一种极其浪费的方法。因此,尽管您可以包括一个额外的第三方库,实例化一个对象,然后在其上调用方法,但您可能不应该这样做。
KaptajnKold

@Captain Cold:好吧,OP要求简洁而不是内存占用。也许该集合可以重用于其他操作?
Guido Anselmi 2013年

1
当然,即便如此:你会在所有诚实永远做自己?如果我在野外看到它,我会认为它是主要的WTF。
KaptajnKold

1
是的,您是对的(我给了您+1),但它假设此检查未在其他地方进行。如果是在其他几个地方完成和/或更改了测试,则使用Set可能很有意义。我将其留给OP选择最佳解决方案。所有这一切都说,如果这是一种单独的用法,我会同意使用Set应该配戴As Clown的耻辱帽。
Guido Anselmi 2013年

2
我实际上认为选择的答案绝对糟糕,然后再使用Set,因为它绝对不可读。
Guido Anselmi

2

我最喜欢的方法之一是使用underscore.js之类的库...

var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
    return test.type === item;
});

if(isItem) {
    // One of them was true
}

http://underscorejs.org/#some


1
contains可以说是比丹尼斯更好的解决方案some
丹尼斯

1
无需为此使用库:someEC5中Array原型上的一个函数。
KaptajnKold

2
是的,但并非每个人都有EC5支持。另外,我真的很喜欢下划线。:)
jcreamer898

如果已经在使用下划线之类的库,这可能是最简单的方法。否则,仅为一个函数加载整个库就没有意义。
Moshe Katz

2

我发现的另一种方式或另一种很棒的方式是...

if ('a' in oc(['a','b','c'])) { //dosomething }

function oc(a)
{
  var o = {};
  for(var i=0;i<a.length;i++)  o[a[i]]='';
  return o;
}

当然,如您所见,这使事情更进一步,并使它们易于遵循逻辑。

http://snook.ca/archives/javascript/testing_for_a_v

使用〜&& ||之类的运算符 ((),())~~只能在以后的代码中断的情况下使用。您将不知道从哪里开始。因此,可读性大。

如果您必须将其缩短。

('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);

如果你想做逆

('a' in oc(['a','b','c'])) || statement;

2

只需使用一条switch语句而不是if语句:

switch (test.type) {

  case "itema":case "itemb":case "itemc":case "itemd":
    // do your process
  case "other cases":...:
    // do other processes
  default:
    // do processes when test.type does not meet your predictions.
}

Switch 比比较一个条件中的许多条件更快 if


2

对于很长的字符串列表,此想法将节省一些字符(并不是说我会在现实生活中推荐它,但是应该可以)。

选择一个您知道不会在test.type中出现的字符,将其用作定界符,将它们全部粘贴到一个长字符串中并搜索:

if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
  // doSomething
}

如果您的字符串碰巧受到进一步限制,您甚至可以省略定界符...

if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
  // doSomething
}

...但是在这种情况下,您必须小心误报(例如,“ embite”在该版本中会匹配)


2

为了提高可读性,请为测试创建一个函数(是的,一行函数):

function isTypeDefined(test) {
    return test.type == 'itema' ||
           test.type == 'itemb' ||
           test.type == 'itemc' ||
           test.type == 'itemd';
}

然后调用它:


    if (isTypeDefined(test)) {

}
...

1

我认为编写这种if条件时有2个目标。

  1. 简洁
  2. 可读性

因此,有时#1可能是最快的,但是以后我将采用#2以便于维护。根据情况的不同,我经常会选择Walter的答案。

首先,我有一个全局可用的函数作为现有库的一部分。

function isDefined(obj){
  return (typeof(obj) != 'undefined');
}

然后,当我实际上要运行类似于您的if条件时,我将创建一个包含有效值列表的对象:

var validOptions = {
  "itema":1,
  "itemb":1,
  "itemc":1,
  "itemd":1
};
if(isDefined(validOptions[test.type])){
  //do something...
}

它不如switch / case语句那么快,并且比其他一些示例更冗长,但是我经常在代码中的其他地方重用该对象,这很方便。

我在上面制作的一个jsperf样本上搭载了此测试,并添加了一个变体来比较速度。http://jsperf.com/if-statements-test-techsin/6我注意到的最有趣的事情是,Firefox中的某些测试组合甚至比Chrome快得多。


1

这可以通过简单的for循环解决:

test = {};
test.type = 'itema';

for(var i=['itema','itemb','itemc']; i[0]==test.type && [
    (function() {
        // do something
        console.log('matched!');
    })()
]; i.shift());

我们使用for循环的第一部分来初始化您要匹配的参数,第二部分用于停止for循环的运行,第三部分使循环最终退出。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.