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


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

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

(Firefox确实提供了魔力 __count__ 属性,但在版本4的某处删除了。)


1196
2017-09-24 08:56


起源


有关: stackoverflow.com/questions/5223/... - ripper234
不同方式的绩效基准: jsben.ch/#/oSt2p - EscapeNetscape
可能重复 JavaScript对象的长度 - Adam Katz


答案:


要在任何与ES5兼容的环境中执行此操作,例如 节点,Chrome,IE 9 +,FF 4+或Safari 5+:

Object.keys(obj).length

1991
2018-02-03 17:47



不仅仅是Node.js,还有支持ES5的任何环境 - Yi Jiang
BTW ......刚刚运行了一些测试...这个方法在O(n)时间内运行。 for循环并不比这种方法差很多。 ** 悲伤的脸 ** stackoverflow.com/questions/7956554/... - BMiner
-1(-200,如果可以的话)这不仅遍历对象,而且还创建了一个包含所有键的全新数组,因此在回答问题时完全失败。 - GetFree
它看起来要快得多(至少在Chrome 25上): jsperf.com/count-elements-in-object - fserb
@GetFree为什么这么多竖起大拇指?这绝对是编码方面最快的方式。无需额外的方法或库。在代码速度方面,显然它也不是太糟糕。根本不完全失败。 87竖起大拇指失败了。 - Andrew


你可以使用这段代码:

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;

141
2018-04-15 10:48



检查的目的是什么 (Object.prototype.hasOwnProperty.call(obj, k))? - styfle
@styfle如果你使用 对于 循环迭代对象的属性,您还获得原型链中的属性。这就是检查的原因 hasOwnProperty 是必要的。它只返回在对象本身上设置的属性。 - Renaat De Muynck
@styfle为了简单起见,你可以写 obj.hasOwnProperty(k) (我实际上是在我的原帖中做到了这一点,但后来更新了)。 hasOwnProperty 每个对象都可用,因为它是一个对象的一部分 Object的原型,但在极少数情况下,这种方法将被删除或覆盖,您可能会得到意想不到的结果。通过调用它 Object.prototype 它使它更加健壮。使用的原因 call 是因为你想调用方法 obj 而不是在原型上。 - Renaat De Muynck
使用这个版本不是更好吗? developer.mozilla.org/en-US/docs/JavaScript/Reference/... - Xavier Delamotte
@XavierDelamotte你是绝对正确的。虽然我的版本有效,但它是一个非常基本的例子。 Mozilla的代码更安全。 (PS:您的链接也在接受的答案中) - Renaat De Muynck


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

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

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

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


126
2018-05-16 15:24



如果你使用underscore.js,那么你应该使用_.size。好的是,如果你以某种方式从数组切换到对象,反之亦然,结果保持不变。 - douwe
注意:这也与lodash一样 - Jeremy - DeerAngel-org
根据我的理解,lodash通常比下划线更好(尽管他们做类似的事情)。 - Merlyn Morgan-Graham
@ MerlynMorgan-Graham,如果我没记错的话,lodash最初是一个下划线的叉子...... - molson504x
_.keys(obj).length 对我来说效果最好,因为我的返回对象有时是一个没有属性的普通字符串。 _.size(obj) 让我回到弦的长度,而 _.keys(obj).length 返回0。 - Jacob Stamm


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

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

1. ECMAScript的Object.keys()

Object.keys(obj).length; 作品 国内 迭代键以计算临时数组并返回其长度。

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

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

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

3.优化for循环

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

否则,您的代码可以通过制作进行非常轻微的优化 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;

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


66
2017-09-24 09:11



为什么会 var k in myobj 提升表现?据我所知,只有函数在JavaScript中声明了新范围。循环是这个规则的一个例外吗? - Lars Gyrup Brink Nielsen
这更快吗? for (var k in myobj) hasOwn.call(myobj, k) && ++count; 即用简单的&&替换if语句? - Hamza Kubba


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

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


23
2017-09-24 09:13



@hitautodestruct因为他提供了一个解决方案。 - crush
@crush这个答案似乎表明要做的事情而不是直接的解决方案。 - hitautodestruct
@hitautodestruct它建议一个答案:使用add / remove方法递增/递减封装计数。还有另一个答案如下所示。唯一的区别是,Confusion没有提供任何代码。答案不是强制要求仅提供代码解决方案。 - crush
@crush你是对的,但他们不应该从一个问题开始。修正:) - hitautodestruct
它可能并不完美......但与其他“答案”相比,这可能是某些情况下的最佳解决方案 - d.raev


我不知道有任何方法可以做到这一点,但是为了尽量减少迭代,你可以尝试检查是否存在 __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__ 该函数将触发并重新计算。


15
2018-01-17 11:15



注意 Object.prototype.__count__ 正在Gecko 1.9.3中删除: whereswalden.com/2010/04/06/...计数-property对象的- - 是感-移除/ - dshaw
现在Firefox 4已经淘汰了,这个答案现在已经过时了。 Object.__count__ 已经消失,而且也很好。 - Yi Jiang
我不会说答案已经过时了。将值封装在函数中仍然是一个有趣的策略。 - devios1
应该使用原型对象来扩展 - Aaria Carter-Weir


正如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

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


15
2018-03-30 01:11