如何缩短我的条件陈述

我有一个非常长的条件声明,如下所示:

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

我想知道是否可以将这个expression/陈述重构成更简洁的forms。

任何想法如何实现这一目标?

把你的值放到数组中,并检查你的项目是否在数组中:

 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/

这个怎么用? 如果在数组中find一个项目, indexOf返回它的索引。 如果找不到该项目,它将返回-1 。 没有太多的细节, ~是一个按位的NOT运算符 ,它只返回-1

我喜欢使用~捷径,因为它比返回值比较更简洁。 我希望JavaScript会有一个in_array函数直接返回一个布尔types(类似于PHP),但这只是一厢情愿的想法( 更新:现在这样做,它被称为includes ,见上文)。 请注意,jQuery的inArray ,共享PHP的方法签名,实际上模仿了本地indexOffunction(如果索引是真正的后面,在不同的情况下是有用的)。

重要提示:使用代字号的快捷方式似乎有争议,因为有些人强烈地认为代码不够清晰,应该不惜一切代价来避免(请参阅对此答案的评论)。 如果你分享他们的观点,你应该坚持.indexOf(...) >= 0解决scheme。


稍微解释一下:

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 ),我们把余数转到下一列。

现在,你会注意到,当给它的正数添加一个负数时,最不是0的最右边的列总是会有两个1 ,当它们加在一起时会得到2 。 二的二进制表示为10 ,我们把1移到下一列,并把结果放在第一列。 左边的所有其他列只有一行1 ,所以从前一列inheritance的1将再次加起来2 ,然后继续…这个过程重复,直到我们到达最左边在那里,被遗忘的地方无处可去,所以它会溢出,迷失方向,我们只剩下0秒。

这个系统被称为2的补码 。 你可以在这里阅读更多关于这个:

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


现在2的补充速成课程已经结束了,你会注意到-1是唯一一个二进制表示为1的数字。

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

所以,这是一个冗长的说法,如果n-1那么只返回0

你可以使用switch语句,

 switch (test.type) { case "itema": case "itemb": case "itemc": case "itemd": // do something } 

使用科学:你应该做什么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)

结论:

如果可能性很小,并且您知道某些特定事件发生的可能性比您获得最高性能的可能性更大 , switch fall throughif(obj[keyval])

如果可能性很多 ,并且其中任何一个都可能是最常发生的,换句话说, if(obj[keyval])regex如果这适合。

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

如果有新的东西出现,我会更新。

如果您正在比较string并且存在模式,请考虑使用正则expression式。

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

 if (test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd') { do something. } 
 var possibilities = { "itema": 1, "itemb": 1, "itemc": 1, …}; if (test.type in possibilities) { … } 

使用一个对象作为关联数组是相当普遍的事情,但是由于JavaScript没有本地集合,所以也可以使用对象作为便宜的集合。

 if( /^item[ad]$/.test(test.type) ) { /* do something */ } 

或者如果物品不统一,那么:

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

优秀的答案,但你可以通过将其中一个包装在一个函数中,使代码更具可读性。

这很复杂,如果陈述,当你(或其他人)在几年的时间内阅读代码,你将通过扫描find部分了解正在发生的事情。 这种业务逻辑级别的陈述会让你在找出你正在testing的东西的时候偶然发现几秒钟。 像这样的代码,将允许您继续扫描。

 if(CheckIfBusinessRuleIsTrue()) { //Do Something } function CheckIfBusinessRuleIsTrue() { return (the best solution from previous posts here); } 

明确命名你的函数,以便立即明白你正在testing什么,你的代码将更容易扫描和理解。

你可以把所有的答案放到一个Javascript集合中 ,然后在集合上调用.contains()

您仍然需要声明所有内容,但内联呼叫将会更短。

就像是:

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

我最喜欢的方式之一是与一个库,如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

另一种方式或另一种令人敬畏的方式,我发现这是…

 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

使用〜&& ||等运算符 ((),())~~罚款只有当你的代码后来rest。 你不知道从哪里开始。 所以可读性很强。

如果你一定要把它缩短。

 ('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; 

只需使用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大量条件更快

对于很长的string列表,这个想法会节省几个字符(不是说我会在现实生活中推荐它,但它应该工作)。

select一个你知道不会出现在你的test.type中的字符,用它作为分隔符,把它们全部放在一个长string中,然后search:

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

如果你的string恰好被进一步限制,你甚至可以省略分隔符。

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

…但在这种情况下,你必须小心误报(例如,“embite”将在该版本中匹配)

写这种条件的时候我觉得有两个目的。

  1. 简短
  2. 可读性

因为有时候#1可能是最快的,但是我会在第二天进行简单的维护。 根据情况,我会经常selectWalter的答案的变体。

要开始我有一个全球可用的function作为我现有的图书馆的一部分。

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

然后当我真的想运行一个if条件类似于你我会创build一个有效值列表的对象:

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

它不像switch / case语句那么快,比其他一些例子更冗长,但是我经常在代码的其他地方重新使用这个对象,这非常方便。

搭载上面制作的jsperf样本之一,我添加了这个testing和一个比较速度的变化。 http://jsperf.com/if-statements-test-techsin/6我注意到的最有趣的事情是Firefox中的某些testing组合甚至比Chrome更快。;

这可以通过一个简单的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循环,第三部分最终导致循环结束。

为了可读性,创build一个testing函数(是的,一个线路函数):

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

然后调用它:

 … if (isTypeDefined(test)) { … } ...