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

新手上路,请多包涵

在 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
}

或者可以使用 switch 功能:

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,因为它用于公司 Intranet 页面。所以 ['a', 'b', 'c'].indexOf(str) !== -1 不会在没有语法糖的情况下本机工作。

原文由 ErikE 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

ES6 (ES2015) 及更高版本

如果您使用 ECMAScript 6(又名 ES2015)或更高版本,最简洁的方法是构建项目数组并使用 Array.includes

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

这比 indexOf 有一些内在的好处,因为它可以正确地测试列表中是否存在 NaN ,并且可以将缺失的数组元素(例如 [1, , 2] 中的中间元素)匹配到 undefined 。它还将 +0-0 视为相等。 includes 也适用于 JavaScript 类型数组,例如 Uint8Array

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

更高的性能

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

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

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

请注意,您可以将任何可迭代项传递给 Set 构造函数(任何支持 for…of 的内容)。您还可以使用 Array.from(set) 或通过扩展它 [...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 做同样的事情。

注意 Object.defineProperty 不能用于 IE8 及更早版本,或其他非常旧版本的浏览器。但是,它是比 String.prototype.isInList = function() { ... } 更好的解决方案,因为使用这样的简单赋值会在 String.prototype 上创建一个可枚举的属性,这更有可能破坏代码。

Array.indexOf

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

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

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

如果您不想使用上面提到的 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;
 }
 }

修改对象原型的注意事项

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

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

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

注意最后一个问题

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

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

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

原文由 ErikE 发布,翻译遵循 CC BY-SA 4.0 许可协议

您可以致电 indexOf

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

原文由 SLaks 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题