ChatGPT解决这个技术问题 Extra ChatGPT

如何缩短我的条件语句

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

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

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

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

您可以将它们放在一个数组中并使用 in
现在只有当有人可以检查哪个是最快的
这可能会让所有人感到震惊,但 OP 所拥有的是速度上的明显赢家!!!!!!!可能会导致浏览器对此进行了很多优化。结果:(1)如果使用 ||。 (2) switch 语句。 (3) 正则表达式。 (4) ~jsperf.com/if-statements-test-techsin
您也可能以错误的方式处理此问题。在这种情况下,这 4 种类型有一些共同点。它是什么?如果我们把它带到一个更极端的情况下,如果我们需要再添加 10 个类型来匹配这个条件怎么办。还是100?如果还有更多,您可能不会考虑使用此解决方案或其他任何建议的解决方案。您会看到这样的大 if 语句,并认为这是代码异味,这是一个好兆头。如果您可以编写 if (test.your_common_condition),那么您最好的方法就是让这更简洁。在这种情况下更容易理解,并且更具扩展性。

J
Joseph Silber

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

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


虽然使用按位运算符肯定很性感,但它真的比 !== -1 以任何可以想象的方式更好吗?显式布尔逻辑不是比隐式使用零的虚假性更合适吗?
很有技术含量,但我不喜欢它。乍一看并不清楚代码在做什么,这使得它无法维护。我更喜欢“Yuriy Galanter”的回答。
-1 新程序员看到这样的答案,认为这是一种很酷且可以接受的编码方式,然后在 5 年内我必须维护他们的代码并撕毁我的头发
这个习语在我的专业领域的 C#、Java 或 Python 等语言中绝对不常见。而且我只是在这里问了一些当地的 Javascript 专家,他们以前都没有见过它;所以它显然不像你声称的那样普遍。 应始终避免使用更清晰、更常见的 != -1
-1 由于在一种语言中不必要的位摆弄,而这种语言首先并没有真正指定很多关于位表示的内容。另外,这个答案的大部分是解释比特摆弄。如果你必须写 20 段来解释这个 hack,真的能节省时间吗?
Y
Yuriy Galanter

您可以将 switch 语句与fall thru 一起使用:

switch (test.type) {

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

和if基本一样,数组方法的索引要好很多
@kojiro 可悲的是,正确的答案是这个,但它不可能引起人们的注意,而不是令人敬畏的 bitwhise-array 技巧。
我知道这个答案必须在这里,但我不得不向下滚动到底部才能找到它。这正是 switch 语句的设计目的,并适用于许多其他语言。我发现很多人不知道 switch 语句中的“失败”方法。
此解决方案在 Firefox 和 Safari 上是最快的,在 Chrome 上是第二快的(仅次于最初的 ||)。请参阅jsperf.com/if-statements-test-techsin
我认为如果您有很多条件,那么在一个方法中编写这将是很糟糕的......如果在几个条件下都可以,但对于超过 10 个条件,我会使用数组来保持代码整洁。
M
Muhammad Umer

使用科学:你应该按照 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 throughif(obj[keyval]) 中获得最大性能的可能性更大。

如果可能性是很多,并且其中任何一个都可能是最常发生的一种,换句话说,您无法知道哪一种最有可能发生如果合适的话,对象查找 if(obj[keyval])regex 的大部分性能。

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

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


+1 一个非常好的帖子!如果我理解正确,switch case 是最快的方法吗?
在 Firefox 中是的,在 chrome 中它的 if ( ...||...||...)...
如果你真的在这个输入上做了很多循环,它会更快,但如果你有一个非常大的 n (“itemX”字符串的数量)循环,它会非常慢。我破解了 this code generator,您可以使用它来验证(或反驳)。如果 n 很大,obj["itemX"] 非常快。基本上,快速取决于上下文。玩得开心。
所以这是最快的方法,但这有关系吗?
@Mich 不要为了速度而牺牲代码的优雅。这是很多人都会对你说的。最后,只需使用常识。
i
idfah

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

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

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

这个答案在速度方面是赢家jsperf.com/if-statements-test-techsin
当项目进入维护模式时,这也是最容易扩展的(使用 (test.type == 'itemf' && foo.mode == 'detailed') 等规则)
k
kojiro
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) { … }

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


这比 FlyingCat 试图缩短的正常 if 语句短多少?
如果您删除所有空格,@dcarson OP 的 if 语句的条件需要 78 个字符。如果你这样写,我的需要 54:test.type in {"itema":1,"itemb":1,"itemc":1,"itemd":1}。从根本上说,对于每个额外的密钥,他每使用两个地雷就使用四个字符。
但你可以这样做: if(possibilities[test.type]) 并保存整个 2 个字符! :)
M
Matt
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

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

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

“有些人在遇到问题时会想‘我知道,我会使用正则表达式’。现在他们有两个问题。” ——杰米·扎温斯基,1997
@MosheKatz虽然我能理解人们喜欢谈论那句话——人们当然会使用正则表达式来表示完全不合适的事情,但这不是其中之一。在 OP 提供的情况下,这不仅符合标准,而且非常好。正则表达式本质上并不是邪恶的,它的目的是匹配具有明确定义的参数的字符串。
@Thor84no 通常,我会假设提问者实际上并没有尝试匹配第一种情况这样的人为示例,并且现实世界的匹配并不是那么简单,在这种情况下,我认为不会使用 RegEx成为正确的方法。换句话说,如果您的 RegEx 只是一个以竖线字符分隔的选项列表,那么它不会比任何其他建议更具可读性,而且效率可能大大降低。
F
Fran Hoey

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

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

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

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

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


我在这里看到的最佳答案。真的,我看到人们并不关心好的设计原则。只是想快速修复一些东西而忘记改进代码,以便将来可以维护系统!
// CheckIfBusinessRuleIsTrue这样评论怎么样?
G
Guido Anselmi

您可以将所有答案放入 Javascript Set 中,然后在集合上调用 .contains()

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

就像是:

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

这似乎是一种非常浪费的方式来完成 OP 试图做的事情。因此,虽然您可以包含一个额外的第 3 方库、实例化一个对象并在其上调用一个方法,但您可能不应该这样做。
@Captain Cold:OP 要求简洁而不是内存占用。也许该集合可以重复用于其他操作?
当然可以,但即便如此:老实说,你自己会这样做吗?如果我在野外看到这个,我会认为这是一个主要的 WTF。
是的,你是对的(我给了你+1),但它假设这个检查没有在其他地方进行。如果它是在其他几个地方完成的和/或测试发生变化,那么使用 Set 可能是有意义的。我把它留给 OP 来选择最佳解决方案。所有这一切,如果这是一个单独的用法,我会同意使用 Set 将配得上作为小丑的耻辱帽。
实际上,我认为选择的答案绝对糟糕,而且比使用 Set 更糟糕,因为它绝对不可读。
j
jcreamer898

我最喜欢的实现这一点的方法之一是使用诸如 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


contains 可以说是比 some 更好的解决方案
无需为此使用库:some 是 EC5 中 Array 原型上的一个函数。
没错,但并不是每个人都可以使用 EC5 支持。另外,我真的很喜欢下划线。 :)
如果您已经在使用下划线之类的库,这可能是最简单的方法。否则,只为一个函数加载整个库是没有意义的。
M
Muhammad Umer

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

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;

u
unmultimedio

只需使用 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 中的许多条件更快


C
CupawnTae

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

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

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

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

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

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


z
zaph

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

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

然后调用它:

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

s
scunliffe

我认为在编写这种 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 循环运行,第三部分用于导致循环最终退出。