ES6(2015)和up
如果你使用的是ECMAScript 6(也就是ES2015)或更高版本,最干净的方法是构造一个数组的项目,并使用array .includes:
['a', 'b', 'c'].includes('b')
与indexOf相比,这有一些固有的好处,因为它可以正确地测试列表中是否存在NaN,并且可以将缺少的数组元素(如[1,,2]中的中间元素)匹配为undefined。它还将+0和-0视为相等。includes也适用于JavaScript类型数组,如Uint8Array。
如果你担心浏览器的支持(比如IE或Edge),你可以选择Array。在CanIUse.Com上,如果您想要针对缺少include的浏览器或浏览器版本,则需要使用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构造函数(任何支持…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上创建一个可枚举的属性。原型,这更有可能破坏代码。
Array.indexOf
如果您使用的是现代浏览器,indexOf总是有效的。然而,对于IE8和更早的版本,你需要一个polyfill。
如果indexOf返回-1,则该项不在列表中。但是要注意,这个方法不能正确地检查NaN,虽然它可以匹配显式的undefined,但它不能像数组[1,,2]那样将缺失的元素匹配为undefined。
用于indexOf或包含在IE或任何其他缺乏支持的浏览器/版本的Polyfill
如果你不想使用像polyfill这样的服务。如上所述,您总是可以在自己的源代码中包含符合标准的自定义腻子。例如,CoreJs库有一个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;
}
}
关于修改对象原型的注释
然而,我不认为修改字符串。prototype或Array。从长远来看,原型是个不错的策略。在JavaScript中修改对象原型可能会导致严重的错误。你需要决定这样做在你自己的环境中是否安全。主要注意的是迭代一个数组(当array。原型已添加属性)与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方法来避免这个额外的关键问题:
Use of instead of in, for (let x of arr). However, depending on the output target and the exact settings/capabilities of your down-leveling transpiler, this may not be reliable. Plus, unless you can guarantee that all of your code and third-party libraries strictly stick to this method, then for the purposes of this question you'll probably just want to use includes as stated above.
Define your new properties on the prototype using Object.defineProperty(), as this will make the property (by default) non-enumerable. This only truly solves the problem if all the JavaScript libraries or modules you use also do this.
注意最后一个问题
最后,要注意,虽然填充是有意义的,修改对象原型是一个有用的策略,但这种方法偶尔仍然会出现范围问题。
In a browser, each distinct document object is its own new global scope, and in browser JS it is possible to create new documents (such as those used for off-screen rendering or to create document fragments) or to get a reference to another page's document object (such as via inter-page communication using a named-target link) so it's possible in certain (rare?) circumstances that object prototypes won't have the methods you expect them to have—though you could always run your polyfills again against the new global objects...
In Node.js, modifying prototypes of global objects may be safe, but modifying the prototypes of non-global, imported objects could lead to breakage if you ever end up with two versions of the same package being required/imported, because imports of the two versions will not expose the same objects, thus won't have the same object prototypes. That is, your code could work fine until a dependency or sub-dependency uses a different version from the one you expect, and without any of your own code changing, a simple npm install or yarn install could trigger this problem. (There are options to deal with this, such as yarn's resolutions property in the package.json, but that's not a good thing to rely on if you have other options.)