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

mjs*_*mjs 1452 javascript performance properties key count

计算对象的键/属性数的最快方法是什么?它可以在不迭代对象的情况下完成此操作吗?即没有做

var count = 0;
for (k in myobj) if (myobj.hasOwnProperty(k)) count++;
Run Code Online (Sandbox Code Playgroud)

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

Avi*_*lax 2377

要做到这一点__CODE__,比如__CODE__

Object.keys(obj).length
Run Code Online (Sandbox Code Playgroud)

  • -1(-200,如果可以的话)这不仅遍历对象,而且还创建了一个包含所有键的全新数组,因此在回答问题时完全失败. (144认同)
  • BTW ......刚刚运行了一些测试......这种方法在O(n)时间内运行.for循环并不比这种方法差很多.**悲伤的脸**http://stackoverflow.com/questions/7956554/efficiently-counting-the-number-of-keys-properties-of-an-object-in-javascript (56认同)
  • 它看起来要快得多(至少在Chrome 25上):http://jsperf.com/count-elements-in-object (40认同)
  • @GetFree为什么这么多竖起大拇指?这绝对是编码方面最快的方式.无需额外的方法或库.在代码速度方面,显然它也不是太糟糕.根本不完全失败.87竖起大拇指失败了. (29认同)
  • 不仅仅是Node.js,还有支持ES5的任何环境 (8认同)
  • 如果你不介意开销,那好吧:) (4认同)
  • @BMiner这是一种耻辱,但这种方法仍然具有简洁明了的优点. (3认同)
  • 这会迭代对象.具体问题是要求不迭代对象的解决方案. (3认同)
  • @ceram1 在这种情况下,人们只关心 javascript 如何实现 `Object.keys()` 方法,而不管什么是“正常”的。指出 Java 如何做某事与本次对话无关。 (2认同)
  • 即使这会创建一个全新的数组,它仍然很快。这可能是因为它是用原生 C 或 C++ 实现的,所以即使它可能效率不高,因为 C 和 C++ 本质上快得多,它仍然可能更快 (2认同)
  • 如果您有潜在密钥的列表,则尝试所有密钥的速度会快 10 倍。我很惊讶,但测试证明了这一点:https://jsperf.com/count-elements-in-object/22 (2认同)

Ren*_*nck 150

你可以使用这段代码:

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;
    };
}
Run Code Online (Sandbox Code Playgroud)

然后您也可以在旧版浏览器中使用它:

var len = Object.keys(obj).length;
Run Code Online (Sandbox Code Playgroud)

  • @styfle如果使用_for_循环迭代对象的属性,则还会获得原型链中的属性.这就是检查`hasOwnProperty`是必要的原因.它只返回在对象本身上设置的属性. (13认同)
  • @styfle为了使它更简单你可以写`obj.hasOwnProperty(k)`(我实际上在我的原始帖子中做了这个,但稍后更新了).`hasOwnProperty`在每个对象上都可用,因为它是`Object`原型的一部分,但在极少数情况下,这个方法会被删除或覆盖,你可能会得到意想不到的结果.通过从`Object.prototype`调用它,它使它更健壮.使用`call`的原因是因为你想在`obj`而不是在原型上调用方法. (12认同)
  • 使用这个版本不是更好吗?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys (5认同)
  • 检查`(Object.prototype.hasOwnProperty.call(obj,k))`的目的是什么? (2认同)

stu*_*eek 134

如果你正在使用Underscore.js可以使用_.size(感谢@douwe):
_.size(obj)

或者你也可以使用_.keys,这对某些人来说可能更清楚:
_.keys(obj).length

我强烈推荐使用Underscore,它是一个用于执行大量基本操作的紧凑库.只要有可能,它们就会匹配ECMA5并遵循本机实现.

否则我支持@ Avi的答案.我编辑它以添加指向MDC文档的链接,其中包含可以添加到非ECMA5浏览器的keys()方法.

  • 如果你使用underscore.js,那么你应该使用_.size.好的是,如果你以某种方式从数组切换到对象,反之亦然,结果保持不变. (9认同)
  • 注意:这也与lodash一样 (8认同)
  • `_.keys(obj).length`最适合我,因为我的返回对象有时是一个没有属性的普通字符串.`_.size(obj)`给了我字符串的长度,而`_.keys(obj).length`返回0. (6认同)
  • 根据我的理解,lodash通常比下划线更好(尽管他们做类似的事情). (2认同)
  • **O(n) 复杂度**。Lodash 和 Underscore 在内部使用“Object.keys”。如果未定义 Object.keys ,下划线还会将每个键复制到 for..in 循环内的数组中。 (2认同)

Luc*_*125 75

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

所以这里是最常用的替代品:

1. ECMAScript的Object.keys()

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

  • 优点 - 可读且干净的语法.如果本机支持不可用,则除了填充程序之外,不需要库或自定义代码
  • 缺点 - 由于创建数组而导致的内存开销.

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

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

3.优化for循环

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

否则,通过使用klocal(var k)和使用prefix-increment operator(++count)而不是postfix ,可以非常轻微地优化您的代码.

var count = 0;
for (var k in myobj) if (myobj.hasOwnProperty(k)) ++count;
Run Code Online (Sandbox Code Playgroud)

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

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

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


daz*_*act 30

这适用于数组和对象

//count objects/arrays
function count(obj){
        return Object.keys(obj).length
     }
Run Code Online (Sandbox Code Playgroud)

使用循环对对象/数组进行计数

function count(obj){
        var x=0;
        for(k in obj){
          x++;
         }
      return x;
     }
Run Code Online (Sandbox Code Playgroud)

计算对象/数组或字符串的长度

function count(obj){
        if (typeof (obj) === 'string' || obj instanceof String)
        {
          return obj.toString().length;   
        }
        return Object.keys(obj).length
     }
Run Code Online (Sandbox Code Playgroud)


Ste*_*per 28

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

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

Object.keys().length

每秒 20,735 次操作

它非常简单和兼容,运行速度快价格昂贵,因为它创建了一个新的键数组,然后被丢弃。

return Object.keys(objectToRead).length;
Run Code Online (Sandbox Code Playgroud)

遍历键

每秒 15,734 次操作

let size=0;
for(let k in objectToRead) {
  size++
}
return size;
Run Code Online (Sandbox Code Playgroud)

它稍慢,但远不及内存使用量,因此如果您有兴趣针对移动设备或其他小型机器进行优化,它可能会更好。

使用 Map 而不是 Object

每秒 953,839,338 次操作

return mapToRead.size;
Run Code Online (Sandbox Code Playgroud)

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

  • “使用地图而不是对象” - 这是此页面上最有帮助的建议。 (28认同)

Con*_*ion 26

如果您实际遇到性能问题,我建议使用一个函数来包装调用向对象添加/删除属性的调用,该函数也会增加/减少一个适当命名的(size?)属性.

您只需要计算一次属性的初始数量,然后从那里继续.如果没有实际的性能问题,请不要打扰.只需在函数中包含该位代码即可getNumberOfProperties(object)完成.

  • @hitautodestruct它建议一个答案:使用add/remove方法递增/递减封装计数.还有另一个答案如下所示.唯一的区别是,Confusion没有提供任何代码.答案不是强制要求仅提供代码解决方案. (5认同)
  • @hitautodestruct因为他提供了一个解决方案. (4认同)
  • 它可能并不完美......但与其他“答案”相比,这可能是某些情况下的最佳解决方案 (2认同)
  • 到目前为止,这是我看到的唯一解决方案,即 O(1) 恒定时间性能复杂度,因此是回答“不迭代”问题细节的唯一解决方案,因此应该是真实的接受答案。大多数(如果不是所有)其他答案都没有回答,因为它们提供了 O(n) 线性时间性能复杂度;对于调用类似 .keys() 函数的 1-line 解决方案也是如此,因为这样的函数调用是 O(n)。 (2认同)

Luk*_*ett 15

我不知道有任何方法可以做到这一点,但是为了将迭代保持在最低限度,您可以尝试检查__count__是否存在(如果它不存在)(即不是Firefox),那么您可以迭代对象并定义它供以后使用,例如:

if (myobj.__count__ === undefined) {
  myobj.__count__ = ...
}
Run Code Online (Sandbox Code Playgroud)

这样任何支持的浏览器__count__都会使用它,并且只对那些不支持的浏览器执行迭代.如果计数发生变化而您无法执行此操作,则可以始终将其设为一个函数:

if (myobj.__count__ === undefined) {
  myobj.__count__ = function() { return ... }
  myobj.__count__.toString = function() { return this(); }
}
Run Code Online (Sandbox Code Playgroud)

这样你随时可以参考myobj.__count__该函数将触发并重新计算.

  • 现在Firefox 4已经推出,这个答案现在已经过时了.`对象.__ count__`消失了,也很好. (17认同)
  • 请注意,在Gecko 1.9.3中删除了`Object.prototype .__ count__`:http://whereswalden.com/2010/04/06/more-changes-coming-to-spidermonkey-the-magical-__count__-property-的对象 - 是感-移除/ (13认同)

Ben*_*der 15

如Avi Flax所述/sf/answers/342276091/

Object.keys(obj).length
Run Code Online (Sandbox Code Playgroud)

将为您的对象上的所有可枚举属性执行操作,但也包括您可以使用的非可枚举属性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
Run Code Online (Sandbox Code Playgroud)

如上所述,这与浏览器支持相同Object.keys

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

  • 因提及“Object.getOwnPropertyNames”而竖起大拇指,你是这里唯一的一个...... (2认同)

Bel*_*ndu 13

迭代Avi Flax回答Object.keys(obj).length对于没有与之关联的函数的对象是正确的

例:

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

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);});}} */
Run Code Online (Sandbox Code Playgroud)

避免这种情况的步骤:

  1. 不要将函数放在要计算键数的对象中

  2. 使用单独的对象或专门为函数创建一个新对象(如果你想计算文件中有多少函数使用Object.keys(obj).length)

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

文档可以在http://underscorejs.org/找到,以及它在github上的来源和各种其他信息

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

_.size(obj)


Fla*_*ken 10

如以上回答: Object.keys(obj).length

但是:正如我们现在在ES6中拥有一个真正的Map类一样,我建议使用它而不是使用对象的属性。

const map = new Map();
map.set("key", "value");
map.size; // THE fastest way
Run Code Online (Sandbox Code Playgroud)


hak*_*nin 8

对于那些在项目中包含Underscore.js的人,您可以:

_({a:'', b:''}).size() // => 2
Run Code Online (Sandbox Code Playgroud)

或功能风格:

_.size({a:'', b:''}) // => 2
Run Code Online (Sandbox Code Playgroud)


lep*_*epe 6

来自: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;
    }
});
Run Code Online (Sandbox Code Playgroud)

或者单个对象:

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

例:

var myObj = {};
myObj.name  = "John Doe";
myObj.email = "leaked@example.com";
myObj.length; //output: 2
Run Code Online (Sandbox Code Playgroud)

添加这种方式,它不会显示在for..in循环中:

for(var i in myObj) {
     console.log(i + ":" + myObj[i]);
}
Run Code Online (Sandbox Code Playgroud)

输出:

name:John Doe
email:leaked@example.com
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您要扩展内置原型或填充属性(即猴子补丁),请正确执行:为了向前兼容性,首先检查属性是否存在,然后使属性不可枚举,以便自己的键的构造物没有被污染。对于方法,请使用 _actual_ [方法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions)。我的建议:遵循[这些示例](/sf/answers/3254389561/),它们演示了如何添加行为尽可能类似于内置方法的方法。 (2认同)

Cli*_*ote 5

我如何解决这个问题是建立我自己的基本列表实现,它记录了对象中存储了多少项.它非常简单.像这样的东西:

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];
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 列表应该是有序且可迭代的.数据存储在对象中,因此无法保证元素的排序.你怎么找到带有洞的列表的长度?this.count?指数值最高?如果您在同一索引处添加两个项目,则计数将进入错误状态. (2认同)