关于性能:如何有效地计算javascript中对象的键/属性的数量?

How to efficiently count the number of keys/properties of an object in JavaScript?

计算对象的键/属性数量的最快方法是什么?是否可以在不遍历对象的情况下执行此操作?也就是说,不做

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

(火狐的确提供了一个神奇的__count__属性,但在第4版左右删除了这个属性。)


在任何一个ES5-compatible environment中这样做,例如[Node](http://nodejs.org), Chrome, IE 9+, FF 4+, or Safari 5+:

1
Object.keys(obj).length
  • 浏览器兼容性
  • 关键文件
    • (包括可以添加到EDCOX1×6浏览器的方法)


你可以使用这个代码:

1
2
3
4
5
6
7
8
9
10
11
12
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;
    };
}

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

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


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

或者,您也可以使用一些更清晰的按键:_.keys(obj).length

我强烈推荐使用下划线,它是一个用于执行许多基本操作的紧密库。只要可能,它们就会匹配ECMA5并遵从本机实现。

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


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

因此,以下是最常用的替代方案:

1。ecmascript的object.keys()。

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

  • pros—可读和清晰的语法。如果本机支持不可用,则不需要库或自定义代码,只需要填充程序
  • cons—由于创建数组而导致的内存开销。

2。基于库的解决方案

本主题其他地方的许多基于库的示例都是在其库上下文中有用的习语。然而,从性能的角度来看,与完美的无库代码相比,没有什么可获得的,因为所有这些库方法实际上都封装了for循环或es5 EDOCX1(native或shimmed)。

三。优化for循环

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

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

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

另一个想法依赖于缓存EDCOX1×10的方法:

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

在给定的环境下,这是否更快是一个标杆问题。无论如何,可以预期非常有限的性能增益。


如果您实际遇到性能问题,我建议使用一个函数来包装向对象添加/从对象中删除属性的调用,该函数还可以增加/减少一个适当命名的(大小?)。财产。

您只需要计算一次初始属性数量,然后从那里继续。如果没有实际的性能问题,就不用麻烦了。只需将这段代码包装在一个函数getNumberOfProperties(object)中,然后就可以完成了。


我不知道有什么方法可以做到这一点,但是为了将迭代次数保持在最低限度,您可以尝试检查EDOCX1[1]的存在性,如果它不存在(即不是firefox),那么您可以对对象进行迭代,并定义它供以后使用,例如:

1
2
3
if (myobj.__count__ === undefined) {
  myobj.__count__ = ...
}

这样,任何支持__count__的浏览器都会使用它,并且只会对那些不支持的浏览器进行迭代。如果计数发生变化而您不能这样做,您可以始终使它成为一个函数:

1
2
3
4
if (myobj.__count__ === undefined) {
  myobj.__count__ = function() { return ... }
  myobj.__count__.toString = function() { return this(); }
}

这样,只要引用myobj.__count__,函数就会启动并重新计算。


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

1
Object.keys(obj).length

将对对象上的所有可枚举属性进行处理,但也包括不可枚举属性,您可以使用Object.getOwnPropertyNames。区别在于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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具有相同的浏览器支持。

但是,在大多数情况下,您可能不希望在这些类型的操作中包含非数字项,但了解它们之间的区别总是很好的;)


对AVI FLASH应答对象进行迭代。键(OBJ)。对于没有与之关联的函数的对象,长度是正确的。

例子:

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

对战

1
2
3
4
5
6
7
8
9
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);});}} */

避免这种情况的步骤:

  • 不要将函数放入要计算键数的对象中

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

  • 同样,我在示例中使用了nodejs中的或下划线模块

    文档可在以下位置找到:http://underlinejs.org/及其在Github上的来源和各种其他信息

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

    _.size(obj)


    对于项目中包含underline.js的用户,可以执行以下操作:

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

    或功能风格:

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


    发件人:https://developer.mozilla.org/en/docs/web/javascript/reference/global_objects/object/defineproperty

    Object.defineProperty(obj, prop, descriptor)

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

    1
    2
    3
    4
    5
    6
    Object.defineProperty(Object.prototype,"length", {
        enumerable: false,
        get: function() {
            return Object.keys(this).length;
        }
    });

    或单个对象:

    1
    2
    3
    4
    5
    6
    7
    var myObj = {};
    Object.defineProperty(myObj,"length", {
        enumerable: false,
        get: function() {
            return Object.keys(this).length;
        }
    });

    例子:

    1
    2
    3
    4
    var myObj = {};
    myObj.name  ="John Doe";
    myObj.email ="[email protected]";
    myObj.length; //output: 2

    这样,它就不会在循环中显示为..

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

    输出:

    1
    2
    name:John Doe
    email:leaked@example.com

    注意:它不适用于


    如上所述:Object.keys(obj).length

    但是:因为我们现在在ES6中有一个真正的map类,所以我建议使用它,而不是使用对象的属性。

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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];
       }
    }


    对于项目中有ExtJS4的用户,可以执行以下操作:

    1
    Ext.Object.getSize(myobj);

    这样做的好处是,它可以在所有与ext兼容的浏览器(包括ie6-ie8)上工作,但是,我相信运行时间并不比o(n)好,就像其他建议的解决方案一样。


    您可以使用Object.keys(data).length来查找具有键数据的JSON对象的长度。


    如果上面的jquery不起作用,请尝试

    1
    $(Object.Item).length


    我认为这是不可能的(至少在没有使用内部结构的情况下是不可能的)。我不认为通过优化这一点你会得到很多。


    我试图使它对所有这样的对象都可用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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

    谷歌关闭有一个很好的功能…goog.object.getcount(对象)

    查看goog.object文档


    你可以使用:Object.keys(objectName).length;
    & Object.values(objectName).length;


    您还可以使用

    1
    JSON.parse(array).lenght