如何有效地计算一个对象在JavaScript中的键/属性的数量?

计算对象的键/属性数的最快方法是什么? 它可以做到这一点,而不是迭代对象? 即没有做

var count = 0; for (k in myobj) if (myobj.hasOwnProperty(k)) count++; 

(Firefox确实提供了一个神奇的__count__属性,但是在版本4的某个地方被删除了。)

要在任何与ES5兼容的环境(如Node ,Chrome,IE 9 +,FF 4 +或Safari 5+)中执行此操作,请执行以下操作:

 Object.keys(obj).length 
  • 浏览器兼容性
  • Object.keys文档
    • (包括可以添加到非ECMA5浏览器的方法)

你可以使用这个代码:

 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; }; } 

或者你可以这样做(在现代浏览器中):

 var len = Object.keys(obj).length; 

如果您使用的是Underscore.js,您可以使用_.size (谢谢@douwe):
_.size(obj)

另外,你也可以使用_.keys这可能会更清晰一些:
_.keys(obj).length

我强烈推荐Underscore,它是一个用于处理大量基本事物的紧密库。 只要有可能,他们都会匹配ECMA5并推迟到本地实施。

否则,我支持@阿维的答案。 我编辑它添加一个链接到MDC文档,其中包括您可以添加到非ECMA5浏览器的keys()方法。

标准的对象实现( ES5.1对象的内部属性和方法 )不需要一个对象来跟踪它的键/属性的数量,所以不应该有标准的方法来确定一个对象的大小,而不需要明确地或者隐式地迭代它的关键。

所以这里是最常用的select:

1. ECMAScript的Object.keys()

Object.keys(obj).length; 通过内部迭代键来计算临时数组并返回其长度。

  • 优点 – 可读和干净的语法。 如果本地支持不可用,则不需要库或自定义代码(除了垫片)
  • 缺点 – 由于创build数组而导致的内存开销。

2.基于图书馆的解决scheme

本主题中其他许多基于库的示例在其库的上下文中都是有用的习语。 然而,从性能的angular度来看,没有任何东西可以与完美的无库代码相比,因为所有这些库方法实际上封装了for-loop或ES5 Object.keys (native或shimmed)。

3.优化一个for循环

这种for循环的最慢部分通常是.hasOwnProperty()调用,因为函数调用开销。 所以当我只想要一个JSON对象的条目数时,我只是跳过.hasOwnProperty()调用,如果我知道没有代码做,也不会扩展Object.prototype

否则,通过使用k local( var k )和使用前缀递减运算符( ++count )而不是postfix,可以对代码进行非常轻微的优化。

 var count = 0; for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count; 

另一个想法依赖于cachinghasOwnProperty方法:

 var hasOwn = Object.prototype.hasOwnProperty; var count = 0; for (var k in myobj) if (hasOwn.call(myobj, k)) ++count; 

在特定的环境下这是否更快是一个基准问题。 无论如何,预计性能上的提高是非常有限的。

如果你真的遇到了一个性能问题,我会build议使用一个函数来增加/减less一个适当命名的(size?)属性。

你只需要计算一次属性的初始数量,然后从那里继续。 如果没有实际的性能问题,请不要打扰。 只需在函数getNumberOfProperties(object)包装这些代码并完成它。

我不知道有什么办法做到这一点,但为了保持迭代到最低限度,你可以尝试检查__count__的存在,如果它不存在(即不是Firefox),那么你可以迭代对象和定义它以备后用,例如:

 if (myobj.__count__ === undefined) { myobj.__count__ = ... } 

这样任何支持__count__浏览器都会使用它,只有那些不支持的迭代才会被执行。 如果计数改变,你不能这样做,你总是可以做一个函数:

 if (myobj.__count__ === undefined) { myobj.__count__ = function() { return ... } myobj.__count__.toString = function() { return this(); } } 

这种方式随时引用myobj。 __count__函数会触发并重新计算。

如Avi Flax所述https://stackoverflow.com/a/4889658/1047014

 Object.keys(obj).length 

将为您的对象的所有可枚举属性做的伎俩,但也包括不可枚举的属性,你可以使用Object.getOwnPropertyNames 。 以下是区别:

 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具有相同的浏览器支持

但是,在大多数情况下,您可能不希望在这些types的操作中包含不可枚举的数据,但是了解其差异总是很好的;)

在Avi Flax上迭代答案Object.keys(obj).length对于没有函数绑定的对象是正确的

例:

 obj = {"lol": "what", owo: "pfft"}; Object.keys(obj).length; // should be 2 

 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);});}} */ 

避免这种情况的步骤:

  1. 不要把函数放在你想要计算键的数量的对象中

  2. 使用一个单独的对象或专门为函数创build一个新的对象(如果要使用Object.keys(obj).length文件中有多less个函数)

也是的,我在我的例子中使用nodejs的_或下划线模块

文档可以在这里findhttp://underscorejs.org/以及它的源在github和各种其他信息

最后是lodash实现https://lodash.com/docs#size

_.size(obj)

对于那些在他们的项目中包含Underscore.js的人,你可以这样做:

 _({a:'', b:''}).size() // => 2 

或function风格:

 _.size({a:'', b:''}) // => 2 

我如何解决这个问题是build立我自己的基本列表的实现,它保存了对象中存储了多less项目的logging。 它非常简单。 像这样的东西:

 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]; } } 

来自: https : //developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty(obj,prop,descriptor)

您可以将其添加到所有对象:

 Object.defineProperty(Object.prototype, "length", { enumerable: false, get: function() { return Object.keys(this).length; } }); 

或者一个对象:

 var myObj = {}; Object.defineProperty(myObj, "length", { enumerable: false, get: function() { return Object.keys(this).length; } }); 

例:

 var myObj = {}; myObj.name = "John Doe"; myObj.email = "leaked@example.com"; myObj.length; //output: 2 

补充说,它不会显示在for..in循环中:

 for(var i in myObj) { console.log(i + ":" + myObj[i]); } 

输出:

 name:John Doe email:leaked@example.com 

注意:它在<IE9浏览器中不起作用。

对于那些在他们的项目中有Ext JS 4的人,你可以这样做:

 Ext.Object.getSize(myobj); 

这样做的好处是,它可以在所有兼容Ext的浏览器(包括IE6-IE8)上运行,但是我相信运行时间并不比O(n)好,就像其他build议的解决scheme一样。

我不认为这是可能的(至less不是没有使用一些内部)。 而且我不认为通过优化这个可以获得很多收益。

如果上面的jQuery不起作用,然后尝试

 $(Object.Item).length 

谷歌closures有一个很好的function… goog.object.getCount(obj)

看看goog.Object文档