如何缩短我的有条件陈述

我有一个很长的 If判断语句,比如说:

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

我想知道我是否可以将这个表达/语句重构成一个更简洁的形式。

有什么办法吗?

22453 次浏览

将您的值放入一个数组,并检查您的项目是否在数组中:

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

如果您支持的浏览器没有 Array#includes方法,则可以使用 这个填充物


~波峰捷径的简短解释:

更新: 既然我们现在有了 includes方法,那么再使用 ~方法就没有意义了。只是为了那些有兴趣了解它是如何工作的人和/或在别人的代码中遇到过它的人保留这些信息。

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

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

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

这是怎么回事?如果在数组中找到某个项,则 indexOf返回其索引。如果没有找到该项,它将返回 -1。没有进入太多的细节,~是一个 按位非运算符,它将返回 0只为 -1

我喜欢使用 ~快捷方式,因为它比对返回值进行比较更简洁。我希望 JavaScript 能有一个直接返回布尔值的 in_array函数(类似于 PHP) ,但这只是一厢情愿的想法(现在有了 更新:函数)。它叫 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) ,我们将余数转移到下一列。

现在,正如您所注意到的,当把一个负数加到它的正数上时,最右边的列并不是所有的 0将总是有两个 1,当它们加在一起时将得到 2。二进制表示为 10,我们将 1带到下一列,并将结果放在第一列的 0中。所有其他左边的列只有一行带有 1,所以从前一列结转过来的 1将再次加到 2,然后再结转... ... 这个过程不断重复,直到我们到达最左边的列,在那里要结转的 1无处可去,所以它溢出并丢失,我们留下了所有的 0

这个系统叫做 2的补语,你可以在这里了解更多:

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


现在2的补语中的速成课程已经结束了,您将注意到 -1是唯一一个二进制表示全部为 1的数字。

使用 ~位非运算符,给定数字中的所有位都被反转。把 0从所有位反转回来的唯一方法是我们从 1的所有位开始。

因此,所有这些都是一种冗长的说法,即如果 n-1,那么 ~n将只返回 0

你可以在 fall through 中使用 switch 语句:

switch (test.type) {


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

使用对象作为关联数组是很常见的事情,但是由于 JavaScript 没有原生集,所以你也可以使用对象作为廉价集。

我最喜欢的实现方法之一是使用 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 (test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd') {
do something.
}
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

如果物品不是那么统一,那么:

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

我发现的另一种或者另一种很棒的方式是这个..。

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;

你可以把所有的答案放在一个 Javascript 设置,然后只要调用 .contains()的设置。

您仍然需要声明所有的内容,但是内联调用会更短。

比如:

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

使用科学: 你应该按照 idfah 所说的去做,这样做是为了最快的速度,同时保持代码简短:

这比 ~方法快

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

Http://jsperf.com/if-statements-test-techsin enter image description here (上集: Chrome,下集: Firefox)

结论:

如果 可能性是 很少,并且您知道某些可能性比 if ||switch fall throughif(obj[keyval])更可能发生,则获得最大性能。

如果 可能性是 很多,并且其中任何一个都可能是最有可能发生的,换句话说,你不知道哪一个是最有可能发生的,比你从对象查找 if(obj[keyval])regex中获得的最高性能要好。

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

如果有新情况,我会更新的。

非常好的答案,但是您可以通过将其中一个包装在函数中来使代码更具可读性。

这是一个复杂的 if 语句,当您(或其他人)在一年的时间内阅读代码时,您将通过扫描来找到部分来理解正在发生的事情。具有这种业务逻辑级别的语句将导致您在确定正在测试的内容时出现几秒钟的问题。而像这样的代码,将允许您继续扫描。

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


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

明确地命名你的函数,这样你正在测试的东西就会立刻显而易见,你的代码也会更容易被扫描和理解。

只需使用 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中比较大量的条件句工作得更快

对于非常长的字符串列表,这个想法可以节省一些字符(不是说我会在现实生活中推荐它,但它应该可以工作)。

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

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

如果您的字符串碰巧受到进一步的约束,您甚至可以省略分隔符..。

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

但在这种情况下,你必须小心假阳性(例如,“ embit”在这个版本中会匹配)

我认为在写这种 if 条件时有两个目标。

  1. 简洁
  2. 可读性

因此,有时候 # 1可能是最快的,但我会采取 # 2以便稍后维护。根据不同的情况,我通常会选择沃尔特的答案的不同版本。

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

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 更快。

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

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

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

那就称之为:

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