检查变量是否等于值列表中的任意一个值

207

我正在检查一个变量,比如foo,是否等于多个值之一。例如:

if( foo == 1 || foo == 3 || foo == 12 ) {
    // ...
}

问题在于这是一个相对微不足道的任务而需要大量的代码。我想到了以下解决方案:

if( foo in {1: 1, 3: 1, 12: 1} ) {
    // ...
}

但是这种方法也不完全符合我的需求,因为我必须为对象中的每个项提供冗余值。

是否有人知道一种良好的方法来检查多个值的相等性?


我认为在做出这样的决定时需要考虑更大的背景,因为了解为什么要进行这样的比较非常重要。 - Pointy
在我所创建的游戏中,我正在检查键盘代码以决定应调用哪个函数。显然,在不同的浏览器中,按键有不同的键码,因此需要与多个值进行比较。 - pimvdb
1
检查多种方法的性能测试,逻辑运算符胜出。 - Pramendra Gupta
1
这是一个常见的重复问题,询问为什么 variable === value1 || value2 不起作用。对于那些想知道原因的人:||&& 使用短路求值;每个操作数都是真或假x === 1 || 2 意味着 (x === 1) || (2),它要么是 true 要么是 2,因为 || 的结果是第一个真值或最后一个假值操作数,而 x === "a" || "b" 要么是 true 要么是 "b"。由于 true2"b" 都是真值,将它们放在 if 条件中等同于 if(true)。只需使用 [ "a", "b" ].includes(x) - Sebastian Simon
请看:为什么我的条件总是为真 - Sebastian Simon
16个回答

242

在ECMA2016中,你可以使用includes方法。这是我见过的最干净的方法。(被所有主要的浏览器支持)

if([1,3,12].includes(foo)) {
    // ...
}

9
对于变量,如果foo[a,b,c]之中,或者对于字符串,如果foo['a','b','c']之中,则执行下列语句。 - Hastig Zusammenstellen
对于变量 => if([a,b,c].includes(foo)) ,因为引号会将其转换为字符串。@HastigZusammenstellen - iambinodstha
10
@BinodShrestha,我想那就是我写的,除非我漏掉了什么。“对于变量...或字符串...” - Hastig Zusammenstellen

218

你可以使用一个数组和indexOf

if ([1,3,12].indexOf(foo) > -1)

1
我喜欢这个。我想通过原型创建一个“包含”函数也是可能的,这样就可以消除使用> -1的情况了。 - pimvdb
1
请注意,由于indexOf仅在ECMAScript第5版后才可用,因此您可能需要自己实现。 - Gumbo
3
你可以使用 ~ 运算符代替 > -1if ( ~[...].indexOf(foo) ) { ... } - Vahid
@vanowm,您暗示'!=='比'!='更费力,但实际上它的工作量更少(例如在底层引擎中类似于x == -1" || x == '-1')?此外,更短并不一定更好-清晰的含义才是更好的,而且预期的含义应该是“不是-1”,那么为什么不在代码中这样表达呢? - Max Waterman
@MaxWaterman,你怎么会认为检查两个条件比检查一个条件更省事呢?我对这一点感到困惑。显然,整个示例的目的是让代码更短,而不是更清晰,否则数组将在单独的变量中。无论如何,每个人都有自己的喜好,我发现使用>运算符比使用!==更容易阅读。 - vanowm
显示剩余4条评论

10

使用提供的答案,我最终得到了以下结果:

Object.prototype.in = function() {
    for(var i=0; i<arguments.length; i++)
       if(arguments[i] == this) return true;
    return false;
}

可以这样调用:

if(foo.in(1, 3, 12)) {
    // ...
}

编辑:我最近发现了这个“技巧”,如果值是字符串并且不包含特殊字符,它会很有用。对于包含特殊字符的情况,由于需要转义,代码变得很难看,而且更容易出错。


/foo|bar|something/.test(str);

更精确地说,这将检查确切的字符串,但对于简单的相等性测试来说可能会更复杂:

/^(foo|bar|something)$/.test(str);

8
天啊!我很惊讶那个能运行——in 是Javascript中的一个关键字。例如,如果运行function in() {}; in() 将会导致语法错误,至少在Firefox中是这样的,我不确定为什么你的代码却没有出错。 :-) - Ben Blank
10
通常被认为扩展Object.prototype是一种不好的做法,因为它会对for (foo in bar)产生不良影响。也许可以将其更改为function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }并将对象作为参数传递? - Ben Blank
7
扩展Object.prototype是一种反模式,不应使用。请改用函数代替。 - Vernon
2
如上所述,通过这种方式扩展Object.prototype存在严重的缺点,可能会导致许多错误。如果您真的想要扩展Object.prototype,请始终通过Object.defineProperty进行,而不设置描述符中的enumerable - Pinke Helga
2
@BenBlank - 只是提供信息,使用关键字作为文字属性名称在第5版规范(2009年12月)中变得有效,这就是为什么 x.in() 起作用的原因。在第5版之前,您必须使用计算属性名称(x["in"]())。 - T.J. Crowder
显示剩余4条评论

8

这个很容易扩展和阅读:

switch (foo) {
    case 1:
    case 3:
    case 12:
        // ...
        break

    case 4:
    case 5:
    case 6:
        // something else
        break
}

但不一定更容易 :)

我实际上使用了这种方法,因为我想在每个值旁边放置一个注释来描述该值相关的内容,这是最可读的实现方式。 - pholcroft
如果您需要描述每个值所关联的内容,您应该创建一个枚举或类,并编写具有描述性名称的属性。 - red_dorian

8
现在可能有更好的解决方案来解决这种情况,但我更喜欢的另一种方式。
const arr = [1,3,12]
if( arr.includes(foo)) { // it will return true if you `foo` is one of array values else false
  // code here    
}

我更喜欢上面的解决方案,而不是使用indexOf检查需要检查索引的方法。

includes文档

if ( arr.indexOf( foo ) !== -1 ) { }

这两个解决方案已经被发布过了。 - undefined

7
您可以这样写:if(foo in L(10,20,30)),如果您定义了L
var L = function()
{
    var obj = {};
    for(var i=0; i<arguments.length; i++)
        obj[arguments[i]] = null;

    return obj;
};

3
请问需要翻译成简体中文还是繁体中文? - pimvdb

6
var a = [1,2,3];

if ( a.indexOf( 1 ) !== -1 ) { }

注意,indexOf不是核心ECMAScript的一部分。你需要一个适用于IE和可能不支持Array.prototype.indexOf的其他浏览器的代码片段。
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(searchElement /*, fromIndex */)
  {
    "use strict";

    if (this === void 0 || this === null)
      throw new TypeError();

    var t = Object(this);
    var len = t.length >>> 0;
    if (len === 0)
      return -1;

    var n = 0;
    if (arguments.length > 0)
    {
      n = Number(arguments[1]);
      if (n !== n)
        n = 0;
      else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
    }

    if (n >= len)
      return -1;

    var k = n >= 0
          ? n
          : Math.max(len - Math.abs(n), 0);

    for (; k < len; k++)
    {
      if (k in t && t[k] === searchElement)
        return k;
    }
    return -1;
  };
}

这是Mozilla的直接副本。太懒了不想链接。https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf - meder omuraliev
如果你并没有实际进行位移操作,那么在定义len时进行位移的原因是什么?这里是否存在某种隐式强制转换? - jaredad7
请注意,indexOf不在核心ECMAScript中。它是在2009年12月的第5版规范中添加的。 - T.J. Crowder

5
此外,由于您检查结果的值都是唯一的,因此您也可以使用Set.prototype.has()

var valid = [1, 3, 12];
var goodFoo = 3;
var badFoo = 55;

// Test
console.log( new Set(valid).has(goodFoo) );
console.log( new Set(valid).has(badFoo) );


3

如果你需要经常比较的一组较大的值,最优的做法可能是先构造一个Set,然后使用Set#has进行常数时间检查,而不是使用线性时间运行的Array#includes

const values = new Set([1, 2, 12]);
if(values.has(foo)){
   // do something
}

以下是有关该主题的一些有用基准测试:https://dev59.com/rV0Z5IYBdhLWcg3wwyne#69125167/ - Jason R Stevens CFA

1
如果您可以访问Underscore,您可以使用以下内容:
if (_.contains([1, 3, 12], foo)) {
  // ...
}

contains在Lodash V4之前也可以使用,现在必须使用includes

if (_.includes([1, 3, 12], foo)) {
  handleYellowFruit();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接