计算对象的键/属性数量的最快方法是什么?是否可以在不遍历对象的情况下执行此操作?也就是说,不做
1 2
| var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) count++; |
(火狐的确提供了一个神奇的__count__属性,但在第4版左右删除了这个属性。)
- 相关:stackoverflow.com/questions/5223/…
- 不同方式的性能基准:jsben.ch//ost2p
- javascript对象的长度可能重复
在任何一个ES5-compatible environment中这样做,例如[Node](http://nodejs.org), Chrome, IE 9+, FF 4+, or Safari 5+:。
- 如果你不介意开销,那就好了:)
- 不仅仅是node.js,还有任何支持ES5的环境
- 顺便说一句。。。刚刚做了一些测试…此方法在O(N)时间内运行。for循环并不比这个方法差多少。**悲伤的脸**stackoverflow.com/questions/7956554/…
- @B我觉得很遗憾,但是这种方法仍然具有简洁和清晰的优点。
- 它一定是O(N),因为它正在创建一个新的大小N的列表。也就是说,var o = {a: 1, b: 2}; Object.keys(o).slice(1)不会影响O。但我仍然认为没有比这更好的答案!
- -1(-200,如果可以的话)这不仅迭代对象,还创建了一个包含所有键的全新数组,因此它完全无法回答问题。
- 它似乎比执行for(至少在chrome 25上)快得多:jspef.com/count-elements-in-object
- @很有趣!也许是一些优化?FF"Loop2"的结果是一个有趣的异常值——比其他任何东西快5倍。
- @fserb是的,但只有当你反复叫它…基本上测试函数调用的运行时间。添加了一个缓存测试,只调用一次object.keys。同样的速度。
- @MJS可能是错误的优化,在FFX21上不再更快。
- @你确定吗?map.keys()的返回值是否反映了.keys()调用后对map所做的更改?
- @=javascript;您似乎混淆了这两者。
- @在这种情况下,人们只关心javascript是如何实现Object.keys()方法的,而不管什么是"正常的"。指出Java做某事与此对话无关。
- @Timtisdall嗯。我认为它会混淆新手,所以我会删除关于Java的评论。Object.keys。根据规范ES5.github.io/x15.2.3.14。它没有指定它是克隆的还是原始的。在chrome中,它说"由于结果是可变的,所以我们必须在每次调用时创建一个新的克隆。"很抱歉造成混淆,但是可以使用object.keys,因为固定数组非常快。
- 这个问题现在已经存在几年了,但是对于任何关注上面速度测试结果的人来说,我使用lodash而不是下划线运行了完全相同的测试:jspef.com/count-elements-in-object/12 lodash kicks-underline's ass
- @杰克塞勒斯:我一定是在误解一些显而易见的东西,但是"下划线"和"洛达什"有什么区别?此外,在我的平台上,两种变体的性能大致相同(均略高于18 kops/s)。
- @collapsar,我不想深入其中,但是一个致力于下划线的开发人员不喜欢下划线功能开发的不一致性(它们如何工作,它们重新设计什么等),并决定创建自己的lib lodash(ba dum tish)。它通常更快,有更多的功能,只是简单的更好(IMO)。如果你有这样的倾向,你可以在两个不同的董事会之间找到讨论(论点),这两个委员会给出了分裂发生的原因。
- 它不会在IE8上工作
- 尽管这样创建了一个全新的数组,但它仍然很快。这可能是因为它是在原生的C或C++中实现的,所以即使它可能不是那么高效,因为C和C++在本质上是更快得多,它仍然可能更快。
- 在CouCHDB中也解决了类似的问题:"ListByVistIyID":{"函数"(doc){if(doc.Type===GHGRePATH))TEMIT(doc.UnviStyIdID,Obj.KEY(doc.RealsReal.)长度);}和ZWnJ;&8203;}
- 我有一个很难解释我的问题,但是假设我有一个扩展我的对象的函数原型,我只想计算具有正常值的属性(例如:string,int)
- 不适用于遍历解析的JSON对象。
- @为什么这么大拇指?这绝对是编码方面最快的方法。不需要额外的方法或库。在代码速度方面,显然也不算太差。一点也不完全失败。87个大拇指向上不适合你。
- 这将迭代对象。具体的问题是寻求不在对象上迭代的解决方案。
- @getfree取决于JS环境如何实现对象,计算对象拥有的属性数量可能不可避免地是关于属性数量的O(N)操作。如果是这样,这就意味着显式地或非显式地遍历所有属性是最有效的方法。(我不知道JS环境如何实现对象,所以这是我的猜测。)
你可以使用这个代码:
1 2 3 4 5 6 7 8 9 10 11 12
| if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],
k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
} |
然后您也可以在旧浏览器中使用它:
1
| var len = Object.keys(obj).length; |
- 支票的目的是什么?
- @Styfle如果使用for循环迭代对象的属性,那么还可以在原型链中获得属性。这就是为什么检查hasOwnProperty是必要的。它只返回对象本身设置的属性。
- 我想我很困惑,因为你在hasOwnProperty上使用call,而不是仅仅使用Object.prototype.hasOwnProperty(obj, k)。这样做的目的是什么?
- @为了简单起见,您只需编写obj.hasOwnProperty(k)(我在最初的文章中确实这样做了,但后来又进行了更新)。每个对象上都有hasOwnProperty可用,因为它是Object原型的一部分,但在极少数情况下,此方法将被删除或重写,您可能会得到意外的结果。通过从Object.prototype中调用它,它使它变得不那么健壮。使用call的原因是您希望在obj上而不是在原型上调用该方法。
- 使用这个版本不是更好吗?developer.mozilla.org/en-us/docs/javascript/reference/…
- @Xavierdelamotte你是绝对正确的。当我的版本工作时,它是一个非常基本的例子。Mozilla的代码更安全。(附言:你的链接也在接受的答案中)
- 对于新加入JS的人来说,看看这个答案…永远不要修改默认的javascript类!
- @dmitry此代码不修改默认类。它只添加一个垫片来模拟不存在的标准功能。
- @RenaAtdemuynck,现在告诉我当名为"keys"的方法成为ecmascript中的标准时会发生什么?当你在考虑它可能有什么规格的时候…
- @dmitrymatveev它已经是一个标准(ecma international.org/ecma-262/5.1/sec-15.2.3.14)。此代码在缺少的地方添加了标准行为。
- @renaatdemuynck stackoverflow.com/questions/14034180/…
- @dmitrymatveev也许我没有明确我的观点:除了实现缺少的标准功能(请仔细阅读以下内容:developer.mozilla.org/nl/docs/web/javascript/…)之外,扩展本机对象原型不是一个好的实践。因此,正如我在之前的评论中所说:Object.keys现在是一个标准的en,应该以这种方式在旧的浏览器中实现。我建议你再仔细阅读一遍,然后再对此发表评论…
如果您使用的是underline.js,则可以使用u.size(谢谢@douwe):_.size(obj)
或者,您也可以使用一些更清晰的按键:_.keys(obj).length
我强烈推荐使用下划线,它是一个用于执行许多基本操作的紧密库。只要可能,它们就会匹配ECMA5并遵从本机实现。
否则我支持@avi的答案。我编辑了它以添加到MDC文档的链接,其中包括可以添加到非ECMA5浏览器的keys()方法。
- 如果您使用underline.js,那么您应该使用u.size。好的是,如果您以某种方式从一个数组切换到另一个对象,或者相反,结果保持不变。
- _尺寸是个不错的提示,我会更新我的答案。
- 注:这同样适用于罗达什
- 根据我的理解,罗达什通常比下划线更好(尽管他们也做类似的事情)。
- @如果我没记错的话,罗达什原本是一个下划线的叉子…
- _.keys(obj).length最适合我,因为我的返回对象有时是一个没有属性的普通字符串。_.size(obj)返回字符串的长度,而_.keys(obj).length返回0。
- o(n)复杂性。lodash和underline在内部使用Object.keys。如果没有定义Object.keys,则下划线还会将每个键复制到for..in循环内的数组中。
标准对象实现(ES5.1对象内部属性和方法)不需要Object来跟踪其键/属性的数量,因此在没有显式或隐式迭代其键的情况下,不应该有标准方法来确定Object的大小。
因此,以下是最常用的替代方案:
1。ecmascript的object.keys()。
Object.keys(obj).length;通过内部迭代键来计算临时数组并返回其长度。
- pros—可读和清晰的语法。如果本机支持不可用,则不需要库或自定义代码,只需要填充程序
- cons—由于创建数组而导致的内存开销。
2。基于库的解决方案
本主题其他地方的许多基于库的示例都是在其库上下文中有用的习语。然而,从性能的角度来看,与完美的无库代码相比,没有什么可获得的,因为所有这些库方法实际上都封装了for循环或es5 EDOCX1(native或shimmed)。
三。优化for循环
这种for循环最慢的部分通常是.hasOwnProperty()调用,因为函数调用开销很大。因此,当我只需要JSON对象的条目数时,如果我知道没有任何代码可以扩展或扩展Object.prototype,那么就跳过.hasOwnProperty()调用。
否则,可以通过使k本地(var k本地)和使用前缀增量运算符(++count而不是后缀来略微优化代码。
1 2
| var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count; |
另一个想法依赖于缓存EDCOX1×10的方法:
1 2 3
| var hasOwn = Object.prototype.hasOwnProperty;
var count = 0;
for (var k in myobj) if (hasOwn.call(myobj, k)) ++count; |
在给定的环境下,这是否更快是一个标杆问题。无论如何,可以预期非常有限的性能增益。
- 为什么var k in myobj会提高性能?据我所知,只有函数在javascript中声明新的作用域。循环中是否存在此规则的异常?
- 这样快吗?for (var k in myobj) hasOwn.call(myobj, k) && ++count;即用简单的if语句替换if语句。
如果您实际遇到性能问题,我建议使用一个函数来包装向对象添加/从对象中删除属性的调用,该函数还可以增加/减少一个适当命名的(大小?)。财产。
您只需要计算一次初始属性数量,然后从那里继续。如果没有实际的性能问题,就不用麻烦了。只需将这段代码包装在一个函数getNumberOfProperties(object)中,然后就可以完成了。
- 因为他提供了一个解决方案。
- 粉碎这个答案似乎暗示着事情要做,而不是给出一个直接的解决办法。
- "HiToAutoStutt"提出了一个答案:用添加/移除方法递增/递减封装的计数。下面还有一个完全一样的答案。唯一的区别是,混乱没有提供任何代码。答案不一定只提供代码解决方案。
- @你是对的,但他们不应该从一个问题开始。固定:
- 它可能并不完美…但与其他"答案"相比,这可能是某些情况下的最佳解决方案。
- 到目前为止,这是我看到的唯一解决方案,即O(1)恒定时间性能复杂性,因此是唯一解决"不重复"问题细节的解决方案,因此应该是TRU接受的答案。大多数(如果不是所有的)其他答案都没有回答这个问题,因为它们提供了一个O(n)线性时间性能复杂性;对于调用类似于.keys()函数的1行解决方案也是如此,因为这样的函数调用是O(n)。
我不知道有什么方法可以做到这一点,但是为了将迭代次数保持在最低限度,您可以尝试检查EDOCX1[1]的存在性,如果它不存在(即不是firefox),那么您可以对对象进行迭代,并定义它供以后使用,例如:
1 2 3
| if (myobj.__count__ === undefined) {
myobj.__count__ = ...
} |
这样,任何支持__count__的浏览器都会使用它,并且只会对那些不支持的浏览器进行迭代。如果计数发生变化而您不能这样做,您可以始终使它成为一个函数:
1 2 3 4
| if (myobj.__count__ === undefined) {
myobj.__count__ = function() { return ... }
myobj.__count__.toString = function() { return this(); }
} |
这样,只要引用myobj.__count__,函数就会启动并重新计算。
- 请注意,gecko 1.9.3中删除了Object.prototype.__count__:其中walden.com/2010/04/06/…count对象的属性是‌&8203;ing/
- 既然火狐4已经过时了,这个答案现在已经过时了。以东十一〔一〕已经不见了,也得到了很好的解脱。
- 我不会说答案过时了。在函数中封装一个值仍然是一个有趣的策略。
- 应该使用原型对象来扩展
如Avi Flax所述https://stackoverflow.com/a/4889658/1047014
将对对象上的所有可枚举属性进行处理,但也包括不可枚举属性,您可以使用Object.getOwnPropertyNames。区别在于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var myObject = new Object();
Object.defineProperty(myObject,"nonEnumerableProp", {
enumerable: false
});
Object.defineProperty(myObject,"enumerableProp", {
enumerable: true
});
console.log(Object.getOwnPropertyNames(myObject).length); //outputs 2
console.log(Object.keys(myObject).length); //outputs 1
console.log(myObject.hasOwnProperty("nonEnumerableProp")); //outputs true
console.log(myObject.hasOwnProperty("enumerableProp")); //outputs true
console.log("nonEnumerableProp" in myObject); //outputs true
console.log("enumerableProp" in myObject); //outputs true |
如本文所述,它与Object.keys具有相同的浏览器支持。
但是,在大多数情况下,您可能不希望在这些类型的操作中包含非数字项,但了解它们之间的区别总是很好的;)
对AVI FLASH应答对象进行迭代。键(OBJ)。对于没有与之关联的函数的对象,长度是正确的。
例子:
1 2
| obj = {"lol":"what", owo:"pfft"};
Object.keys(obj).length; // should be 2 |
对战
1 2 3 4 5 6 7 8 9
| arr = [];
obj = {"lol":"what", owo:"pfft"};
obj.omg = function(){
_.each(obj, function(a){
arr.push(a);
});
};
Object.keys(obj).length; // should be 3 because it looks like this
/* obj === {"lol":"what", owo:"pfft", omg: function(){_.each(obj, function(a){arr.push(a);});}} */ |
避免这种情况的步骤:
不要将函数放入要计算键数的对象中
使用单独的对象或为函数专门创建一个新对象(如果要计算使用Object.keys(obj).length的文件中有多少函数)
同样,我在示例中使用了nodejs中的或下划线模块
文档可在以下位置找到:http://underlinejs.org/及其在Github上的来源和各种其他信息
最后是一个lodash实现https://lodash.com/docs size
_.size(obj)
- 作为对你关于Array(obj).length的评论的回应:它不起作用。http://jsfiddle.net/jhy8米/
- 是的,我仔细看了一下,如果可能的话,我会把这个答案删除,或者只是把它编辑在一起。
- 编辑我的答案来重复AVI亚麻回答
- 我没有看到这与函数有任何关系,比如说,在Chrome中,我根本看不到这种行为。我怀疑这可能与object.defineproperty():Enumerable有关,它是false,尽管我还没有找到任何关于var obj = { a: true, b: true }与var obj = {}; obj.a = true; obj.b = true;的不同之处的文档,或者只是如果chrome采用了w3的不同解释/语义。
对于项目中包含underline.js的用户,可以执行以下操作:
1
| _({a:'', b:''}).size() // => 2 |
或功能风格:
1
| _.size({a:'', b:''}) // => 2 |
发件人:https://developer.mozilla.org/en/docs/web/javascript/reference/global_objects/object/defineproperty
Object.defineProperty(obj, prop, descriptor)
您可以将其添加到所有对象中:
1 2 3 4 5 6
| Object.defineProperty(Object.prototype,"length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
}); |
或单个对象:
1 2 3 4 5 6 7
| var myObj = {};
Object.defineProperty(myObj,"length", {
enumerable: false,
get: function() {
return Object.keys(this).length;
}
}); |
例子:
1 2 3 4
| var myObj = {};
myObj. name ="John Doe";
myObj. email ="[email protected]";
myObj. length; //output: 2 |
这样,它就不会在循环中显示为..
1 2 3
| for(var i in myObj) {
console.log(i +":" + myObj[i]);
} |
输出:
1 2
| name:John Doe
email:leaked@example.com |
注意:它不适用于
如上所述:Object.keys(obj).length。
但是:因为我们现在在ES6中有一个真正的map类,所以我建议使用它,而不是使用对象的属性。
1 2 3
| const map = new Map();
map.set("key","value");
map.size; // THE fastest way |
我解决这个问题的方法是建立我自己的基本列表实现,它记录对象中存储了多少项。很简单。像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function BasicList()
{
var items = {};
this.count = 0;
this.add = function(index, item)
{
items[index] = item;
this.count++;
}
this.remove = function (index)
{
delete items[index];
this.count--;
}
this.get = function(index)
{
if (undefined === index)
return items;
else
return items[index];
}
} |
- 有趣的选择。这确实消除了聚合计数函数的开销,但每次添加或删除元素时都会以函数调用为代价,不幸的是,这可能会更糟。我个人可以使用这样的列表实现来进行数据封装,以及与普通数组相比它可以提供的自定义方法,但是当我只需要快速的项目计数时就不需要了。
- 我喜欢你的回答,但我也是一个狐猴和点击上票。这提出了一个有趣的难题。在你的指示中,你没有考虑到一些行为,比如我已经对你的答案投了反对票,但是我被指示"点击反对票",而不能。这条指令无声地失败了,但我从你的内容中收集到了一些信息,所以无声地失败并不是你喜欢代码所做的事情。抬头一点。
- 我真的很喜欢这个答案,很好的数据结构。如果对add函数调用有性能影响,那么如果必须遍历一个对象,那么性能会大大提高。这应该允许最快的循环模式var i = basiclist.countwhile(i--){...}。
- 基本清单至少应该包括基本检查吗?比如检查add是否替换旧项,或者是否使用不存在的索引调用remove。此外,如果undefined是有效的项目值,则无法检查列表是否具有给定的索引。
- 难道get不应该有if (undefined === index)吗?由于测试是相反的,因此将使用undefined索引来访问数组。
- 这太可怕了。如果它被称为列表,它应该表现得像一个。这就像是一个列表和一个没有做好任何事情的对象之间的奇怪混合。我更愿意坚持基本的数据类型。
- "最后一杯羹"在哪里不象列表?
- 一个列表应该是有序的和可重复的。数据存储在对象中,因此无法保证元素的顺序。你如何找到一个有洞的长度列表?这个?伯爵?最高的索引值?如果在同一索引中添加两个项,则计数将进入错误状态。
- @最后的惩罚是公平的。尽管它被称为"基本主义",但它并不是一个完结的全部。它旨在扩展和定制。
对于项目中有ExtJS4的用户,可以执行以下操作:
1
| Ext.Object.getSize(myobj); |
这样做的好处是,它可以在所有与ext兼容的浏览器(包括ie6-ie8)上工作,但是,我相信运行时间并不比o(n)好,就像其他建议的解决方案一样。
您可以使用Object.keys(data).length来查找具有键数据的JSON对象的长度。
如果上面的jquery不起作用,请尝试
我认为这是不可能的(至少在没有使用内部结构的情况下是不可能的)。我不认为通过优化这一点你会得到很多。
- 接受的答案表明,这是可以做到的,你没有理由断言没有任何东西可以获得。
我试图使它对所有这样的对象都可用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Object.defineProperty(Object.prototype,"length", {
get() {
if (!Object.keys) {
Object.keys = function (obj) {
var keys = [],k;
for (k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) {
keys.push(k);
}
}
return keys;
};
}
return Object.keys(this).length;
},});
console.log({"Name":"Joe","Age":26}.length) //returns 2 |
谷歌关闭有一个很好的功能…goog.object.getcount(对象)
查看goog.object文档
你可以使用:Object.keys(objectName).length;
& Object.values(objectName).length;
您还可以使用
1
| JSON.parse(array).lenght |