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

计算对象的键/属性数量的最快方法是什么?是否可以在不迭代对象的情况下做到这一点?即,不做:

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

(Firefox确实提供了一个神奇的__count__属性,但在版本4左右被删除了。

931104 次浏览

如果您实际上遇到了性能问题,我建议将向对象添加/删除属性的调用包装为一个函数,该函数还会增加/减少适当命名的(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__时,函数都会触发并重新计算。

要在任何ES5兼容环境中执行此操作,例如Node.js,Chrome,Internet Explorer 9+,Firefox 4+或Safari5+:

Object.keys(obj).length

您可以使用此代码:

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,则可以使用_尺寸谢谢douwe):

_.size(obj)

或者,您也可以使用_钥匙,这对某些人来说可能更清晰:

_.keys(obj).length

我强烈推荐Underscore.js.这是一个紧凑的库,可以做很多基本的事情。只要有可能,它们就会匹配ECMAScript 5并遵守本机实现。

否则我支持Avi Flax的回答。我编辑它以添加MDC留档的链接,其中包括您可以添加到非ECMAScript 5浏览器的key()方法。

我解决这个问题的方法是构建我自己的基本列表实现,它记录了对象中存储了多少项。这很简单。像这样:

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;elsereturn items[index];}}

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

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

或功能风格:

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

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

Ext.Object.getSize(myobj);

这样做的好处是它可以在所有与Ext JS兼容的浏览器(包括Internet Explorer 6-Internet Explorer 8)上运行。但是,我相信运行时间并不比O(n)好,就像其他建议的解决方案一样。

如果以前的答案中的jQuery不起作用,请尝试

$(Object.Item).length

作者:Avi Flax

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 2console.log(Object.keys(myObject).length); //outputs 1
console.log(myObject.hasOwnProperty("nonEnumerableProp")); //outputs trueconsole.log(myObject.hasOwnProperty("enumerableProp")); //outputs true
console.log("nonEnumerableProp" in myObject); //outputs trueconsole.log("enumerableProp" in myObject); //outputs true

这里指出一样,它具有与Object.keys相同的浏览器支持。

但是,在大多数情况下,您可能不想在这些类型的操作中包含不可枚举项,但了解差异总是好的;)

标准Object实现(ES5.1对象内部属性和方法)不需要Object来跟踪其键/属性的数量,因此不应该有标准的方法来确定Object的大小,而无需显式或隐式地迭代其键。

以下是最常用的替代品:

1. ECMAScript的Object.keys()

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

  • 优点-可读且干净的语法。如果本机支持不可用,则不需要库或自定义代码,除了垫片
  • 缺点-由于创建数组而产生的内存开销。

2.基于图书馆的解决方案

本主题其他地方的许多基于库的示例在其库的上下文中是有用的习语。然而,从性能的角度来看,与完美的无库代码相比没有任何好处,因为所有这些库方法实际上封装了for循环或ES5Object.keys(本机或shimmed)。

3.优化for循环

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

否则,您的代码可以通过使k本地(var k)和使用前缀-增量运算符(++count)而不是后缀来稍微优化。

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

另一个想法依赖于缓存hasOwnProperty方法:

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

在给定的环境中,这是否更快是一个基准测试的问题。无论如何,可以预期的性能提升非常有限。

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

示例:

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. 使用单独的对象或专门为函数创建一个新对象(如果您想使用Object.keys(obj).length计算文件中有多少函数)

此外,是的,我在示例中使用了Node.js的_Underscore.js模块。

文档可以在GitHub上找到这里及其来源和各种其他信息。

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

_.size(obj)

Object.define属性()

Object.define属性(obj、prop、描述符)

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

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(var i in myObj) {console.log(i + ": " + myObj[i]);}

输出:

name: John Doeemail: leaked@example.com

注意:它在Internet Explorer 9之前的浏览器中不起作用。

如前一个答案所示:Object.keys(obj).length

但是:由于我们现在在ES6中有一个真正的地图类,我想建议使用它而不是使用对象的属性。

const map = new Map();map.set("key", "value");map.size; // THE fastest way

您可以使用:

Object.keys(objectName).length;

Object.values(objectName).length;

我尝试让它对所有对象都可用,如下所示:

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

OP没有指定对象是否为nodeList。如果是,则可以直接对其使用长度方法。例子:

buttons = document.querySelectorAll('[id=button)) {console.log('Found ' + buttons.length + ' on the screen');

以下是三种方法的一些性能测试;

https://jsperf.com/get-the-number-of-keys-in-an-object

Object.keys()

每秒20 735次操作

它非常简单且兼容,运行速度快昂贵,因为它创建了一个新的密钥数组,然后将其丢弃。

return Object.keys(objectToRead).length;

循环遍历密钥

每秒15 734次操作

let size=0;for(let k in objectToRead) {size++}return size;

它稍微慢一些,但远没有达到内存使用率,所以如果您有兴趣优化移动或其他小型机器,可能会更好。

使用Map代替Object

每秒953,839,338次操作

return mapToRead.size;

基本上,Map跟踪它自己的大小,所以我们只是返回一个数字字段。它比任何其他方法都快得多。如果您可以控制对象,请将它们转换为map。

这适用于数组和对象

//count objects/arraysfunction count(obj){return Object.keys(obj).length}

使用循环计数对象/数组

function count(obj){var x=0;for(k in obj){x++;}return x;}

计数对象/数组以及String的长度

function count(obj){if (typeof (obj) === 'string' || obj instanceof String){return obj.length;}return Object.keys(obj).length}