EcmaScript 6及以上版本
如果您使用的是ES6或更高版本,最干净的方法是构造一个项目数组并使用Array.includes
:
['a', 'b', 'c'].includes('b')
这比indexOf
有一些固有的好处,因为它可以正确地测试列表中是否存在NaN
,并且可以匹配缺失的数组元素,例如[1, , 2]
到undefined
中的中间元素.includes
也适用于JavaScript typed arrays,比如Uint8Array
.
如果您担心浏览器支持(例如IE或Edge),您可以 Select check Array.includes
at CanIUse.Com,如果您想要针对缺少includes
的浏览器或浏览器版本,我建议使用polyfill.io填充.
更高的性能
请注意,Array.includes()
的时间随数组中元素的数量而变化:它的性能为O(n).如果您需要更高的性能,并且不会重复构建项目集(但会重复判断项目是否包含某些元素),那么应该使用Set
.
const interestingItems = new Set(['a', 'b', 'c'])
const isItemInSet = interestingItems.has('b')
请注意,您可以将任何可迭代项传递给Set
构造函数(支持for...of的任何项).您还可以使用Array.from(set)
将Set
转换为array.
没有数组
不建议这样做,但您可以按如下方式向字符串添加新的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
上创建一个可枚举属性,这更有可能 destruct 代码.
Array.indexOf
如果您使用的是现代浏览器,则indexOf
始终有效.但是,对于IE8和更早版本,您将需要一个多边形填充.
如果indexOf
返回-1,则该项不在列表中.但是请注意,该方法不会正确判断NaN
,虽然它可以匹配显式undefined
,但它不能像数组[1, , 2]
中那样将缺失的元素匹配到undefined
.
Polyfill for indexOf
or includes
in IE, or any other browser/version lacking support
如果您不想使用上面提到的像polyfill.io这样的服务,您可以始终在自己的源代码中包含符合标准的自定义多边形填充.例如,Mozilla开发者网络(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.prototype
或Array.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库或插件时,如果该库或插件不积极防范继承的密钥,一切都可能崩溃.
避免这种 destruct 的老方法是,在枚举过程中,判断每个值,看看对象是否真的将其作为非继承属性,使用if (arr.hasOwnProperty(x))
和only then处理x
.
避免这一额外关键问题的新ES6方法有:
用of
代替in
,for (let x of arr)
.但是,根据输出目标和您的下层转换机的确切设置/功能,这可能不可靠.另外,除非您能保证您的所有代码和第三方库都严格遵循此方法,否则对于此问题,您可能只想使用上面所述的includes
.
使用Object.defineProperty()
在原型上定义新属性,因为这将使属性(默认情况下)不可枚举.只有当您使用的所有JavaScript库或模块也这样做时,这才真正解决了问题.
注意最后一个问题
最后,请注意,虽然多边形填充是有意义的,修改对象原型是一种有用的策略,但这种方法偶尔仍可能存在范围界定问题.
在浏览器中,每个不同的document
对象都是自己的新全局范围,在浏览器JS中,可以创建新文档(例如用于屏幕外渲染或创建文档片段的文档)或获取对另一个页面的document
对象的引用(例如通过使用命名目标链接的页面间通信),因此在某些情况下是可能的(罕见的?)对象原型不会有你期望的方法,尽管你总是可以对新的全局对象再次运行多边形填充...
在node中,修改global
个对象的原型可能是安全的,但如果最终需要/导入同一软件包的两个版本,修改非全局导入对象的原型可能会导致 destruct ,因为两个版本的导入不会expose 相同的对象,因此不会有相同的对象原型.也就是说,在依赖项或子依赖项使用与预期版本不同的版本之前,您的代码可以正常工作,并且在不更改任何代码的情况下,简单的npm install
或yarn install
可能会触发此问题.(解决这个问题有很多 Select ,比如Yarn 在package.json
中的resolutions
属性,但如果你有其他 Select ,这不是一件好事情.)