我有一个很长的条件语句,如下所示:
if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
// do something.
}
我想知道是否可以将此表达式/语句重构为更简洁的形式。
关于如何实现这一目标的任何想法?
in
?
||
。 (2) switch
语句。 (3) 正则表达式。 (4) ~
。 jsperf.com/if-statements-test-techsin
将您的值放入数组中,并检查您的项目是否在数组中:
if ([1, 2, 3, 4].includes(test.type)) {
// Do something
}
如果您支持的浏览器没有 Array#includes
方法,您可以使用 this polyfill。
~ 波浪号快捷方式的简短说明:
更新:由于我们现在有了 include 方法,所以不再使用 ~ hack。只为那些有兴趣了解它的工作原理和/或在其他代码中遇到过它的人保留它。
除了检查 indexOf
的结果是否为 >= 0
,还有一个不错的小捷径:
if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
// Do something
}
这是小提琴:http://jsfiddle.net/HYJvK/
这是如何运作的?如果在数组中找到一个项目,indexOf
返回它的索引。如果未找到该项目,它将返回 -1
。无需过多介绍,~
是一个 bitwise NOT operator,它只会为 -1
返回 0
。
我喜欢使用 ~
快捷方式,因为它比比较返回值更简洁。我希望 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
),我们将余数移至下一列。
现在,您会注意到,当将一个负数添加到其正数时,不是所有 0
的最右边的列将始终有两个 1
,当它们相加时将产生 2
.两个的二进制表示是 10
,我们将 1
带到下一列,并将结果放在第一列中的 0
。左边的所有其他列只有一行带有 1
,因此从前一列结转的 1
将再次加起来为 2
,然后将结转...这个过程不断重复,直到我们到达最左边的一列,要结转的 1
无处可去,所以它溢出并丢失了,我们只剩下 0
了。
该系统称为 2 的补码。您可以在此处阅读有关此内容的更多信息:
2's Complement Representation for Signed Integers。
现在 2 的补码速成课程已经结束,您会注意到 -1
是唯一一个二进制表示为 1
的数字。
使用 ~
按位 NOT 运算符,给定数字中的所有位都被反转。从反转所有位中恢复 0
的唯一方法是,如果我们从 1
的全部开始。
因此,所有这一切都是一种冗长的说法,即如果 n
是 -1
,~n
只会返回 0
。
您可以将 switch 语句与fall thru 一起使用:
switch (test.type) {
case "itema":
case "itemb":
case "itemc":
case "itemd":
// do something
}
||
)。请参阅jsperf.com/if-statements-test-techsin
使用科学:你应该按照 idfah 所说的去做,这样可以在保持代码简短的同时获得最快的速度:
这比 ~
方法快
var x = test.type;
if (x == 'itema' ||
x == 'itemb' ||
x == 'itemc' ||
x == 'itemd') {
//do something
}
https://i.stack.imgur.com/Y69mv.jpg
结论 :
如果可能性很少,并且您知道某些可能性比您在 if ||
、switch fall through
和 if(obj[keyval])
中获得最大性能的可能性更大。
如果可能性是很多,并且其中任何一个都可能是最常发生的一种,换句话说,您无法知道哪一种最有可能发生如果合适的话,对象查找 if(obj[keyval])
和 regex
的大部分性能。
http://jsperf.com/if-statements-test-techsin/12
如果有新的东西出现,我会更新。
switch case
是最快的方法吗?
if ( ...||...||...)...
obj["itemX"]
非常快。基本上,快速取决于上下文。玩得开心。
如果您要与字符串进行比较并且存在模式,请考虑使用正则表达式。
否则,我怀疑试图缩短它只会混淆你的代码。考虑简单地包装线条以使其漂亮。
if (test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd') {
do something.
}
(test.type == 'itemf' && foo.mode == 'detailed')
等规则)
var possibilities = {
"itema": 1,
"itemb": 1,
"itemc": 1,
…};
if (test.type in possibilities) { … }
将对象用作关联数组是很常见的事情,但由于 JavaScript 没有原生集合,您也可以将对象用作廉价集合。
if
语句的条件需要 78 个字符。如果你这样写,我的需要 54:test.type in {"itema":1,"itemb":1,"itemc":1,"itemd":1}
。从根本上说,对于每个额外的密钥,他每使用两个地雷就使用四个字符。
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }
或者如果项目不是那么统一,那么:
if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }
很好的答案,但是您可以通过将其中一个包装在函数中来使代码更具可读性。
这是一个复杂的 if 语句,当您(或其他人)在一年内阅读代码时,您将通过扫描找到该部分以了解正在发生的事情。具有这种业务逻辑级别的语句将导致您在确定要测试的内容时绊倒几秒钟。像这样的代码,将允许您继续扫描。
if(CheckIfBusinessRuleIsTrue())
{
//Do Something
}
function CheckIfBusinessRuleIsTrue()
{
return (the best solution from previous posts here);
}
明确命名您的函数,以便立即清楚您正在测试的内容,并且您的代码将更容易扫描和理解。
// CheckIfBusinessRuleIsTrue
这样评论怎么样?
您可以将所有答案放入 Javascript Set 中,然后在集合上调用 .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
}
contains
可以说是比 some
更好的解决方案
some
是 EC5 中 Array 原型上的一个函数。
我发现的另一种方式或另一种很棒的方式是......
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;
只需使用 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
}
...但在这种情况下你必须小心误报(例如,“embite”会在那个版本中匹配)
为了可读性,为测试创建一个函数(是的,一个单行函数):
function isTypeDefined(test) {
return test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd';
}
然后调用它:
…
if (isTypeDefined(test)) {
…
}
...
我认为在编写这种 if 条件时有两个目标。
简洁的可读性
因此,有时#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 循环运行,第三部分用于导致循环最终退出。
不定期副业成功案例分享
!== -1
以任何可以想象的方式更好吗?显式布尔逻辑不是比隐式使用零的虚假性更合适吗?!= -1
。