Вопрос: Как эффективно подсчитывать количество ключей / свойств объекта в JavaScript?


Каков самый быстрый способ подсчета количества ключей / свойств объекта? Это можно сделать без итерации над объектом? т. е. без

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

(Firefox действительно обеспечил магию __count__свойство, но это было удалено где-то около версии 4.)


1169


источник


Ответы:


Для этого в любой ES5-совместимой среде, такой как Узел , Chrome, IE 9+, FF 4+ или Safari 5+:

Object.keys(obj).length

1947



Вы можете использовать этот код:

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;

139



Если вы используете Underscore.js вы можете использовать _.размер (спасибо @douwe):
_.size(obj)

В качестве альтернативы вы также можете использовать _.keys которые могут быть более ясными для некоторых:
_.keys(obj).length

Я очень рекомендую Underscore, его плотную библиотеку для выполнения множества основных вещей. Когда это возможно, они соответствуют ECMA5 и относятся к исходной реализации.

В противном случае я поддерживаю ответ @ Avi. Я отредактировал его, чтобы добавить ссылку на MDC-документ, который включает в себя метод keys (), который вы можете добавить в браузер, не относящийся к ECMA5.


125



Стандартная реализация объекта ( Внутренние свойства и методы объекта ES5.1 ) не требует Objectдля отслеживания его количества ключей / свойств, поэтому не должно быть стандартного способа определить размер Objectбез явного или неявного повторения его ключей.

Итак, вот наиболее часто используемые альтернативы:

1. Object.keys ECMAScript ()

Object.keys(obj).length;Работает внутренне итерации по клавишам для вычисления временного массива и возврата его длины.

  • Pros - Читаемый и чистый синтаксис. Никакой библиотеки или пользовательского кода не требуется, кроме прокладки, если встроенная поддержка недоступна
  • Cons - Издержки памяти из-за создания массива.

2. Библиотечные решения

Многие примеры, основанные на библиотеке в другом месте этого раздела, являются полезными идиомами в контексте их библиотеки. С точки зрения производительности, однако, нет ничего, что можно было бы сравнить с совершенным кодом без библиотеки, поскольку все эти библиотечные методы фактически инкапсулируют либо для цикла, либо для ES5 Object.keys(нативный или подвешенный).

3. Оптимизация цикла for-loop

самая медленная часть такой петли, как правило, .hasOwnProperty()вызов из-за служебных данных вызова функции. Поэтому, когда мне просто нужно количество записей объекта JSON, я просто пропущу .hasOwnProperty()позвоните, если я знаю, что никакого кода не будет распространяться Object.prototype,

В противном случае ваш код может быть немного оптимизирован, kместный ( var k) и с помощью оператора prefix-increment ( ++count) вместо postfix.

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;

Является ли это быстрее или нет в данной среде, является вопрос бенчмаркинга. В любом случае можно ожидать очень ограниченного прироста производительности.


65



Если вы действительно сталкиваетесь с проблемой производительности, я бы предложил обернуть вызовы, которые добавляют / удаляют свойства в / из объекта с помощью функции, которая также увеличивает / уменьшает свойство с соответствующим именем (size?).

Вам нужно только вычислить начальное число свойств один раз и перейти оттуда. Если проблем с производительностью не существует, не беспокойтесь. Просто обмотайте этот бит кода в функцию getNumberOfProperties(object)и покончить с этим.


23



I'm not aware of any way to do this, however to keep the iterations to a minimum, you could try checking for the existance of __count__ and if it doesn't exist (ie not Firefox) then you could iterate over the object and define it for later use eg:

if (myobj.__count__ === undefined) {
  myobj.__count__ = ...
}

This way any browser supporting __count__ would use that, and iterations would only be carried out for those which don't. If the count changes and you can't do this, you could always make it a function:

if (myobj.__count__ === undefined) {
  myobj.__count__ = function() { return ... }
  myobj.__count__.toString = function() { return this(); }
}

This way anytime you reference myobj.__count__ the function will fire and recalculate.


15



As stated by Avi Flax https://stackoverflow.com/a/4889658/1047014

Object.keys(obj).length

will do the trick for all enumerable properties on your object but to also include the non-enumerable properties you can instead use the Object.getOwnPropertyNames. Here's the difference:

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

As stated here this has the same browser support as Object.keys

However, in most cases, you might not want to include the nonenumerables in these type of operations, but it's always good to know the difference ;)


15



To iterate on Avi Flax answer Object.keys(obj).length is correct for an object that doesnt have functions tied to it

example:

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

versus

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);});}} */

steps to avoid this:

  1. do not put functions in an object that you want to count the number of keys in

  2. use a seperate object or make a new object specifically for functions (if you want to count how many functions there are in the file using Object.keys(obj).length)

also yes i used the _ or underscore module from nodejs in my example

documentation can be found here http://underscorejs.org/ as well as its source on github and various other info

And finally a lodash implementation https://lodash.com/docs#size

_.size(obj)


12



For those who have Underscore.js included in their project you can do:

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

or functional style:

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

8