在 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 许可协议
ES6 (ES2015) 及更高版本
如果您使用 ECMAScript 6(又名 ES2015)或更高版本,最简洁的方法是构建项目数组并使用
Array.includes
:这比
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
)对于读取是次线性的:请注意,您可以将任何可迭代项传递给
Set
构造函数(任何支持 for…of 的内容)。您还可以使用Array.from(set)
或通过扩展它[...set]
将Set
转换为数组。没有数组
这不是真正推荐的,但您可以向字符串添加一个新的
isInList
属性,如下所示:然后像这样使用它:
你可以对
Number.prototype
做同样的事情。注意
Object.defineProperty
不能用于 IE8 及更早版本,或其他非常旧版本的浏览器。但是,它是比String.prototype.isInList = function() { ... }
更好的解决方案,因为使用这样的简单赋值会在String.prototype
上创建一个可枚举的属性,这更有可能破坏代码。Array.indexOf
如果您使用的是现代浏览器,
indexOf
始终有效。但是,对于 IE8 和更早版本,您需要一个 polyfill。如果
indexOf
返回 -1,则该项目不在列表中。但请注意,此方法不会正确检查NaN
,虽然它可以匹配显式undefined
,但它不能将缺少的元素与数组[1, , 2]
中的undefined
匹配。IE 中
indexOf
或includes
的 Polyfill,或任何其他缺乏支持的浏览器/版本如果您不想使用上面提到的 polyfill.io 之类的服务,您始终可以在自己的源代码中包含符合标准的自定义 polyfill。例如,Mozilla Developer Network 有一个
indexOf
。在这种情况下,我必须为 Internet Explorer 7 制定解决方案,我“推出了自己的”不符合标准的
indexOf()
函数的更简单版本:修改对象原型的注意事项
但是,我不认为修改
String.prototype
或Array.prototype
是长期的好策略。在 JavaScript 中修改对象原型可能会导致严重的错误。您需要决定这样做在您自己的环境中是否安全。主要需要注意的是,使用for ... in
迭代数组(当 Array.prototype 添加属性时)将返回新函数名作为键之一:您的代码现在可能可以工作,但是当将来有人添加第三方 JavaScript 库或插件时,这些库或插件并不热心地保护继承的键,一切都可能崩溃。
避免这种损坏的旧方法是,在枚举期间检查每个值,以查看对象是否实际上将其作为非继承属性与
if (arr.hasOwnProperty(x))
一起使用, 然后才 使用该x
。避免这种额外密钥问题的新 ES6 方法是:
使用
of
代替in
,for (let x of arr)
。但是,根据输出目标和下级转译器的确切设置/功能,这可能不可靠。另外,除非您可以保证您的所有代码和第三方库都严格遵守此方法,否则出于此问题的目的,您可能只想使用如上所述的includes
。使用
Object.defineProperty()
在原型上定义新属性,因为这将使属性(默认情况下)不可枚举。只有当您使用的所有 JavaScript 库或模块都这样做时,这才能真正解决问题。注意最后一个问题
最后,请注意,虽然 polyfill 很有意义,并且修改对象原型是一种有用的策略,但这种方法偶尔仍会出现范围问题。
在浏览器中,每个不同的
document
对象都是它自己的新全局范围,在浏览器 JS 中,可以创建新文档(例如用于离屏渲染或创建文档片段的文档)或获取对另一个页面的引用document
对象(例如通过使用命名目标链接的页面间通信),因此在某些(罕见?)情况下,对象原型可能没有您期望的方法 - 尽管您总是可以再次运行您的 polyfill针对新的全局对象…在 Node.js 中,修改
global
对象的原型可能是安全的,但是如果您最终需要/导入同一个包的两个版本,那么修改非全局导入对象的原型可能会导致损坏,因为两个版本不会公开相同的对象,因此不会有相同的对象原型。也就是说,您的代码可以正常工作,直到依赖项或子依赖项使用与您期望的版本不同的版本,并且您自己的代码没有任何更改,简单的npm install
或yarn install
可能会触发此问题。 (有一些选项可以解决这个问题,例如package.json
中 yarn 的resolutions
属性,但如果你有其他选项,这不是一件好事。)