如何有效地计算一个对象在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);});}} */
避免这种情况的步骤:
-
不要把函数放在你想要计算键的数量的对象中
-
使用一个单独的对象或专门为函数创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文档