ES6 (ES2015) 及更高版本
如果您使用的是 ECMAScript 6(又名 ES2015)或更高版本,最干净的方法是构造一个项目数组并使用 Array.include
:
['a', 'b', 'c'].includes('b')
这有一些固有的好处,因为它可以正确地测试列表中是否存在,并且可以将缺失的数组元素(例如中间的数组元素)与 匹配。它也平等地对待和平等。 也适用于 JavaScript 类型化数组,如 .indexOf
NaN
[1, , 2]
undefined
+0
-0
includes
Uint8Array
如果您担心浏览器支持(例如对IE或Edge的支持),则可以在 CanIUse.Com 中检查Array.includes
,如果要定位缺少的浏览器或浏览器版本,则需要使用Babel等工具转码到较低的ECMAScript版本,或者在浏览器中包含polyfill脚本,例如 polyfill.io 中提供的脚本。includes
更高的性能
请注意,它的时间随数组中的元素数量而缩放:它具有性能 O(n)。如果您需要更高的性能,并且不会重复构造项目集(但会反复检查项目是否包含某些元素),则应使用 Set
,因为 ES 规范要求 (以及) 的实现是子线性的读取:Array.includes()
Set
Map
该规范要求实现集合“平均而言,这些集合提供对集合中元素数量亚线性的访问时间”。因此,它可以在内部表示为哈希表(带有O(1)查找),搜索树(带有O(log(N))查找)或任何其他数据结构,只要复杂性优于O(N)。
const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')
请注意,您可以将任何可迭代项传递给构造函数(支持...的)。您还可以使用 或 通过扩展将 a 转换为数组。Set
Set
Array.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.defineProperty
String.prototype.isInList = function() { ... }
String.prototype
Array.indexOf
如果您使用的是现代浏览器,则始终有效。但是,对于IE8及更早版本,您将需要一个polyfill。indexOf
如果返回 -1,则表示该项不在列表中。但请注意,此方法将无法正确检查 ,虽然它可以匹配显式 ,但它无法将缺少的元素与数组中的一样匹配。indexOf
NaN
undefined
undefined
[1, , 2]
IE 或 IE 中或 IE 中 Polyfill,或任何其他缺乏支持的浏览器/版本indexOf
includes
如果您不想使用如上所述的 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.prototype
Array.prototype
for ... 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方法是:
-
请用 来代替 、 。但是,根据输出目标和下调转译器的确切设置/功能,这可能不可靠。另外,除非您可以保证所有代码和第三方库都严格遵守此方法,否则出于此问题的目的,您可能只想如上所述使用。of
in
for (let x of arr)
includes
-
使用 在原型上定义新属性,因为这将使该属性(默认情况下)不可枚举。这只有在您使用的所有JavaScript库或模块也这样做时才真正解决问题。Object.defineProperty()
注意最后一个问题
最后,请注意,虽然 polyfill 是有意义的,并且修改对象原型是一种有用的策略,但这种方法偶尔会存在范围问题。
在浏览器中,每个不同的对象都是它自己的新全局范围,在浏览器JS中,可以创建新文档(例如用于屏幕外呈现或创建文档片段的文档)或获取对另一个页面对象的引用(例如通过使用命名目标链接进行页面间通信),因此在某些(罕见?)情况下,对象原型可能没有您期望它们具有的方法 - 尽管您可以总是对新的全局对象再次运行多边形填充...document
document
在 Node.js 中,修改对象的原型可能是安全的,但是如果您最终需要/导入同一包的两个版本,则修改非全局导入对象的原型可能会导致中断,因为导入两个版本不会公开相同的对象,因此不会具有相同的对象原型。也就是说,您的代码可以正常工作,直到依赖项或子依赖项使用与您期望的版本不同的版本,并且无需更改您自己的任何代码,则简单或可能触发此问题。(有一些选项可以解决这个问题,例如 yarn 在 中的属性,但如果您有其他选择,那么这不是一件好事。global
npm install
yarn install
resolutions
package.json