我想保留一个字符串列表,我只检查是否存在,例如:
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个元素,速度并不重要。但是对于更大的阵列,访问哪种方式会更快?
答案 0 :(得分:26)
在JavaScript中,您通常需要处理各种各样的实现(除非您在受控环境中使用它,例如您选择引擎的服务器),因此特定性能问题的答案往往是“这取决于你在将要使用的引擎上检查它。”一个实现上最快的可能在另一个实现上更慢,等等。http://jsperf.com对于这类事情很方便。
那就是说,我希望in
在这里成为明显的赢家。 Array#indexOf
必须在搜索中访问数组索引,并且数组索引是与任何其他属性一样的属性。因此访问数组索引0
以查看它是否是所需的字符串需要查找0
,就像另一个需要查找属性"canSummonKraken"
一样(之后它必须进行字符串比较) 。 (是的,数组索引是属性.JMAR中的数组aren't really arrays。)indexOf
可能必须在搜索期间访问多个属性,而in
只需要访问一个属性。但同样,你需要在你的目标环境中检查它以确定,一些实现可以优化具有连续索引范围的数组(但最慢的那些肯定不会,当然如果你担心速度,你“我担心最慢的引擎上的速度最快,比如IE浏览器。”
另请注意,并非所有JavaScript引擎都有Array#indexOf
。大多数人都这样做,但仍然有一些较老的人(我正在看着你,微软)不这样做。
您还有使用in
或hasOwnProperty
的问题。使用in
的优点是它是一个运算符,而不是函数调用;使用hasOwnProperty
的优点是它只会查看特定的对象实例,而不是它的原型(及其原型等)。除非你有一个非常深层次的继承层次结构(并且你没有在你的例子中),我敢打赌in
获胜,但记住它确实检查层次结构是有用的。
另外,请记住,"canSummonKraken" in obj
在您显示的示例对象文字中将为true,因为该对象确实具有该属性,即使该属性的值为null。您必须没有所有属性才能返回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
快,但我会测试它。如果列表可能很长并且你将要拥有很多这些对象,那么它很有用,因为它只需要存在“truthy”属性。空对象表示用户无法执行任何操作的计划,并且非常小。
我应该注意两件事:
in
也会检查对象的原型,所以如果你有toString
或valueOf
这样的设置,你会得到误报(因为它们几乎是属性)所有对象都来自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
一样快。 (但请参阅我的开头段落:在您的环境中测试。:-))
答案 1 :(得分:15)
我测试了它:http://jsperf.com/array-indexof-vs-object-s-in-operator/4
找到第一个元素时,两者都有很好的结果,具体取决于正在使用的浏览器。
因此,找到最后一个元素,in
运算符要快得多。
但后来我使用了一个带有typeof运算符的变体,它比两者都快得多:
if (typeof obj['that'] !== "undefined") {
// found
}
答案 2 :(得分:11)
这是一个基准http://jsperf.com/array-indexof-vs-object-keys。 在chrome和firefox中,检查对象中键的存在比扫描数组快100%。
但是如果考虑初始化时间,差异会消除,对象会比数组花费更多时间进行初始化。
答案 3 :(得分:0)
我运行了一个简单得多的测试,该测试仅真正测试:array.indexOf
操作并与object[key]
引用和值here进行比较。
这表明,object[key]
运算符仅涉及1个键并且查找失败时,速度快〜 6倍。
这实际上是对最佳情况数据集(1个条目)的最坏情况查找(失败),应作为大多数此类操作的良好代理。
每个键添加到源Array a
/ Object o
时,速度差距越来越大。
Object
是明显的赢家。
答案 4 :(得分:0)
我清楚地看到Boolean(corporatePlan [item])
const t0 = performance.now();
const a = [], b=[];
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
for(let i=0; i<1000; i++) a.push((i+1)+"");
for(let i=0; i<100; i++) b.push(getRandomInt(10000)+"");
const bmap = a.reduce((map, x)=>{map[x]=1; return map;}, {});
const c = b.filter((x)=>x in bmap);
const t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
// Call to doSomething took 0.360000180080533 milliseconds.
// ---------------------------------------------------------------------------------
const t0 = performance.now();
const a = [], b=[];
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
for(let i=0; i<1000; i++) a.push((i+1)+"");
for(let i=0; i<100; i++) b.push(getRandomInt(10000)+"");
const c = b.filter((x)=>a.indexOf(x) !== -1);
const t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
// Call to doSomething took 0.5349998828023672 milliseconds.
// ---------------------------------------------------------------------------------
const t0 = performance.now();
const a = [], b=[];
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
for(let i=0; i<1000; i++) a.push((i+1)+"");
for(let i=0; i<100; i++) b.push(getRandomInt(10000)+"");
const bmap = a.reduce((map, x)=>{map[x]=1; return map;}, {});
const c = b.filter((x)=>typeof(bmap[x]) !== "undefined");
const t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
// Call to doSomething took 0.3500001039355993 milliseconds.
// ---------------------------------------------------------------------------------
const t0 = performance.now();
const a = [], b=[];
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
for(let i=0; i<1000; i++) a.push((i+1)+"");
for(let i=0; i<100; i++) b.push(getRandomInt(10000)+"");
const bmap = a.reduce((map, x)=>{map[x]=1; return map;}, {});
const c = b.filter((x)=>Boolean(bmap[x]));
const t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
// Call to doSomething took 0.260000117123127 milliseconds.
// ---------------------------------------------------------------------------------
const t0 = performance.now();
const a = [], b=[];
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
for(let i=0; i<1000; i++) a.push((i+1)+"");
for(let i=0; i<100; i++) b.push(getRandomInt(10000)+"");
const bmap = a.reduce((map, x)=>{map[x]=1; return map;}, {});
const c = b.filter((x)=>bmap[x]);
const t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
// Call to doSomething took 0.3450000658631325 milliseconds.
// ---------------------------------------------------------------------------------