确定字符串是否在 JavaScript 的列表中

2022-08-29 23:38:16

在SQL中,我们可以看到字符串是否在列表中,如下所示:

Column IN ('a', 'b', 'c')

在JavaScript中做到这一点的好方法是什么?这样做太笨拙了:

if (expression1 || expression2 || str === 'a' || str === 'b' || str === 'c') {
   // do something
}

我不确定这个性能或清晰度:

if (expression1 || expression2 || {a:1, b:1, c:1}[str]) {
   // do something
}

或者可以使用开关功能:

var str = 'a',
   flag = false;

switch (str) {
   case 'a':
   case 'b':
   case 'c':
      flag = true;
   default:
}

if (expression1 || expression2 || flag) {
   // do something
}

但这是一团糟。有什么想法吗?

在这种情况下,我必须使用Internet Explorer 7,因为它适用于公司内部网页面。所以如果没有一些语法糖,就不能在本地工作。['a', 'b', 'c'].indexOf(str) !== -1


答案 1

ES6 (ES2015) 及更高版本

如果您使用的是 ECMAScript 6(又名 ES2015)或更高版本,最干净的方法是构造一个项目数组并使用 Array.include

['a', 'b', 'c'].includes('b')

这有一些固有的好处,因为它可以正确地测试列表中是否存在,并且可以将缺失的数组元素(例如中间的数组元素)与 匹配。它也平等地对待和平等。 也适用于 JavaScript 类型化数组,如 .indexOfNaN[1, , 2]undefined+0-0includesUint8Array

如果您担心浏览器支持(例如对IE或Edge的支持),则可以在 CanIUse.Com 中检查Array.includes,如果要定位缺少的浏览器或浏览器版本,则需要使用Babel等工具转码到较低的ECMAScript版本,或者在浏览器中包含polyfill脚本,例如 polyfill.io 中提供的脚本。includes

更高的性能

请注意,它的时间随数组中的元素数量而缩放:它具有性能 O(n)。如果您需要更高的性能,并且不会重复构造项目集(但会反复检查项目是否包含某些元素),则应使用 Set,因为 ES 规范要求 (以及) 的实现是子线性的读取:Array.includes()SetMap

该规范要求实现集合“平均而言,这些集合提供对集合中元素数量亚线性的访问时间”。因此,它可以在内部表示为哈希表(带有O(1)查找),搜索树(带有O(log(N))查找)或任何其他数据结构,只要复杂性优于O(N)。

const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')

请注意,您可以将任何可迭代项传递给构造函数(支持...的)。您还可以使用 或 通过扩展将 a 转换为数组。SetSetArray.from(set)[...set]

不带数组

这并不建议这样做,但您可以向字符串添加新属性,如下所示:isInList

if (!String.prototype.isInList) {
  Object.defineProperty(String.prototype, 'isInList', {
    get: () => function(...args) {
      let value = this.valueOf();
      for (let i = 0, l = args.length; i < l; i += 1) {
        if (arguments[i] === value) return true;
      }
      return false;
    }
  });
}

然后像这样使用它:

'fox'.isInList('weasel', 'fox', 'stoat') // true
'fox'.isInList('weasel', 'stoat') // false

您可以对 执行相同的操作。Number.prototype

请注意,不能在IE8及更早版本或其他浏览器的旧版本中使用。但是,这是一个优越得多的解决方案,因为使用像这样的简单赋值将在 上创建一个可枚举属性,这更有可能破坏代码。Object.definePropertyString.prototype.isInList = function() { ... }String.prototype

Array.indexOf

如果您使用的是现代浏览器,则始终有效。但是,对于IE8及更早版本,您将需要一个polyfill。indexOf

如果返回 -1,则表示该项不在列表中。但请注意,此方法将无法正确检查 ,虽然它可以匹配显式 ,但它无法将缺少的元素与数组中的一样匹配。indexOfNaNundefinedundefined[1, , 2]

IE 或 IE 中或 IE 中 Polyfill,或任何其他缺乏支持的浏览器/版本indexOfincludes

如果您不想使用如上所述的 polyfill.io 等服务,您可以随时在自己的源代码中包含符合标准的自定义polyfill。例如,Mozilla Developer Network有一个用于indexOf。

在这种情况下,我必须为Internet Explorer 7制定解决方案,我“推出了自己的”更简单版本的函数,该版本不符合标准:indexOf()

if (!Array.prototype.indexOf) {
   Array.prototype.indexOf = function(item) {
      var i = this.length;
      while (i--) {
         if (this[i] === item) return i;
      }
      return -1;
   }
}

有关修改对象原型的说明

但是,从长远来看,我不认为修改或是一个好的策略。在 JavaScript 中修改对象原型可能会导致严重的错误。您需要确定在您自己的环境中这样做是否安全。主要值得注意的是,迭代数组(当 Array.prototype 添加了属性时)将返回新的函数名称作为键之一:String.prototypeArray.prototypefor ... in

Array.prototype.blah = function() { console.log('blah'); };
let arr = [1, 2, 3];
for (let x in arr) { console.log(x); }
// Result:
0
1
2
blah // Extra member iterated over!

你的代码现在可能工作,但是当将来有人添加一个第三方JavaScript库或插件时,一切都会崩溃。

避免这种中断的旧方法是,在枚举期间,检查每个值,以查看对象是否实际上将其作为非继承属性,然后才使用该值。if (arr.hasOwnProperty(x))x

避免这个额外关键问题的新ES6方法是:

  1. 请用 来代替 、 。但是,根据输出目标和下调转译器的确切设置/功能,这可能不可靠。另外,除非您可以保证所有代码和第三方库都严格遵守此方法,否则出于此问题的目的,您可能只想如上所述使用。ofinfor (let x of arr)includes

  2. 使用 在原型上定义新属性,因为这将使该属性(默认情况下)不可枚举。这只有在您使用的所有JavaScript库或模块也这样做时才真正解决问题。Object.defineProperty()

注意最后一个问题

最后,请注意,虽然 polyfill 是有意义的,并且修改对象原型是一种有用的策略,但这种方法偶尔会存在范围问题。

在浏览器中,每个不同的对象都是它自己的新全局范围,在浏览器JS中,可以创建新文档(例如用于屏幕外呈现或创建文档片段的文档)或获取对另一个页面对象的引用(例如通过使用命名目标链接进行页面间通信),因此在某些(罕见?)情况下,对象原型可能没有您期望它们具有的方法 - 尽管您可以总是对新的全局对象再次运行多边形填充...documentdocument

在 Node.js 中,修改对象的原型可能是安全的,但是如果您最终需要/导入同一包的两个版本,则修改非全局导入对象的原型可能会导致中断,因为导入两个版本不会公开相同的对象,因此不会具有相同的对象原型。也就是说,您的代码可以正常工作,直到依赖项或子依赖项使用与您期望的版本不同的版本,并且无需更改您自己的任何代码,则简单或可能触发此问题。(有一些选项可以解决这个问题,例如 yarn 在 中的属性,但如果您有其他选择,那么这不是一件好事。globalnpm installyarn installresolutionspackage.json


答案 2

您可以致电:indexOf

if (['a', 'b', 'c'].indexOf(str) >= 0) {
    //do something
}