+-
在JS中,速度更快:对象的“ in”运算符或数组的indexof?

我想保留一个字符串列表,我只会检查它的存在,例如:

corporatePlan = [
    'canDeAuthorize',
    'hasGmailSupport',
    'canShareReports',
    'canSummonKraken',
    'etc'
]

因此,当用户尝试召唤海妖时,我将执行corporatePlan.indexof('canSummonKraken') != -1以查看他是否可以。

同事建议将其存储为对象会更快:

"Corporate Plan" = {
    'canDeAuthorize' : null,
    'hasGmailSupport' : null,
    'canShareReports' : null,
    'canSummonKraken' : null,
    'etc' : null
}

然后执行'canSummonKraken' in corporatePlan之类的操作来检查计划是否包含该密钥。从经典的CS角度讲,这是有道理的,因为“包含”当然是在地图上恒定的时间,在数组上是线性的。不过,这是否检查了如何在JS中实现数组和对象?

在我们的特殊情况下,元素少于100个,速度并不重要。但是对于更大的数组,哪种方式访问​​起来会更快?

28
投票

[在JavaScript中,您通常要处理各种各样的实现方式(除非您在受控环境(例如您选择引擎的服务器)中使用它),因此,针对特定性能问题的答案通常是“取决于您要使用的引擎。”一个实现上最快的执行速度可能会慢一些,以此类推。http://jsperf.com对于此类事情非常有用。

就是说,我希望in在这里显然是赢家。 Array#indexOf必须在搜索中访问数组索引,并且数组索引是与其他任何属性一样的属性。因此,访问数组索引0以查看它是否是所需的字符串,需要查找0,就像另一个需要查找属性"canSummonKraken"一样(然后它必须随后进行字符串比较)。 (是的,数组索引是属性。JavaScriptaren't really arrays中的数组完全是。)并且indexOf在其搜索过程中可能必须访问多个属性,而in仅需要访问一个属性。但是同样,您需要在目标环境中进行检查,以确保某些实现可以优化具有连续索引范围的数组(但最慢的数组肯定没有,当然,如果您担心速度,可以'担心最慢的引擎(例如IE引擎)上最快的是什么。

还请注意,并不是所有的JavaScript引擎都具有Array#indexOf。大多数都可以,但是仍然有一些旧的(我在看着你,微软)没有。

您还有使用in还是hasOwnProperty的问题。使用in的优点是它是一个运算符,而不是函数调用;使用hasOwnProperty的优点是,它将仅查看特定的对象实例,而不查看其原型(及其原型等)。除非您有一个非常深层次的继承层次结构(并且在您的示例中没有),否则我相信in会获胜,但是记住它确实会检查层次结构很有用。

[另外,请记住,在您显示的示例对象文字中,"canSummonKraken" in obj将为true,因为该对象确实具有属性,即使该属性的值为null。您不必具有属性[[at all才能使in返回false。 (代替in,您可以只使用true和false并将其查找为obj.canSummonKraken。)

所以您的选择是:

您的数组方法:

corporatePlan = [ 'canDeAuthorize', 'hasGmailSupport', 'canShareReports', 'canSummonKraken', 'etc' ]; console.log(corporatePlan.indexOf("canSummonKraken") >= 0); // true console.log(corporatePlan.indexOf("canDismissKraken") >= 0); // false

...我不推荐。

in方法:

corporatePlan = { 'canDeAuthorize' : null, 'hasGmailSupport' : null, 'canShareReports' : null, 'canSummonKraken' : null, 'etc' : null }; console.log("canSummonKraken" in corporatePlan); // true console.log("canDismissKraken" in corporatePlan); // false

可能比indexOf快,但我会对其进行测试。如果列表可能很长,并且您将要拥有很多这样的对象,则很有用,因为它只要求“真正的”属性存在。空对象表示用户无法执行任何操作的计划,并且非常小。

我应该在这里注意两件事:

in也会检查对象的原型,因此,如果您具有toStringvalueOf之类的设置,则会得到误报(因为这些属性几乎都是从Object.prototype获得的属性) 。在启用ES5的浏览器上,可以通过使用null原型创建对象来避免该问题:var corporatePlan = Object.create(null);

也许是因为它检查原型,所以in运算符在某些引擎上

令人惊讶地缓慢

这两个问题都可以通过使用hasOwnProperty来解决:

console.log(corporatePlan.hasOwnProperty("canSummonKraken")); // true console.log(corporatePlan.hasOwnProperty("canDismissKraken")); // false

一个人会认为运算符会比方法调用更快,但事实证明这并不是真正的跨浏览器。

标志方法:

corporatePlan = { 'canDeAuthorize' : true, 'hasGmailSupport' : true, 'canShareReports' : true, 'canSummonKraken' : true, 'canDismissKraken' : false, 'etc' : true }; console.log(corporatePlan.canSummonKraken); // "true" console.log(corporatePlan.canDismissKraken); // "false" // or using bracketed notation, in case you need to test this // dynamically console.log(corporatePlan["canSummonKraken"]); // "true" console.log(corporatePlan["canDismissKraken"]); // "false" // example dynamic check: var item; item = "canSummonKraken"; console.log(corporatePlan[item]); // "true" item = "canDismissKraken"; console.log(corporatePlan[item]); // "false"

...这将是相当正常的做法,可能比in快,并且可能至少与hasOwnProperty一样快。 (但请参见我的开头段落:在您的环境中进行测试。:-))
15
投票
我已经测试过: http://jsperf.com/array-indexof-vs-object-s-in-operator/4

找到第一个元素时,根据所使用的浏览器,两者都有很好的结果。因此,找到最后一个元素,in运算符要快得多。

但是后来我使用了一个带有typeof运算符的变体,它比两者都快得多:

if (typeof obj['that'] !== "undefined") { // found }

11
投票
这里是基准 http://jsperf.com/array-indexof-vs-object-keys。在chrome和firefox中,检查对象中是否存在键比扫描阵列快100%。

“结果”

但是如果考虑到初始化时间,则差异会抵消,对象比数组花费更多的时间进行初始化。

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS96WGpXZC5wbmcifQ==” alt =“在此处输入图像描述”>

0
投票
对象参考无疑是赢家

我运行了一个简单得多的测试,该测试仅实际测试:array.indexOf操作并与object[key]引用和值here进行比较。

test array.indexOf(key) v. object[key]

这表明,仅涉及1个键并且查找失败时,array.indexOf运算符的速度要慢〜

6X。

这实际上是对最佳情况数据集(1个条目)的最坏情况查找(失败),应该充当大多数此类操作的良好代理。

对于添加到源Array a / Object o的每个键,它都会放慢速度。

Object参考无疑是赢家。