Длина объекта в JavaScript: почему нет простого .length и что делать?
Вы когда-нибудь ловили себя на мысли: «Ну почему у массива есть свойство .length, а у объекта — нет?». Если да — вы не одиноки. Вопрос «Length of a JavaScript object» входит в топ самых популярных на Stack Overflow с тысячами просмотров. И это неудивительно: каждый новичок рано или поздно пытается написать obj.length и получает undefined.
В этой статье мы разберёмся, почему так происходит, и, главное, — как правильно посчитать длину объекта в JavaScript. Вы узнаете 4 рабочих способа, поймёте, когда какой использовать, и навсегда забудете о путанице с длиной объекта.
Почему у объектов нет свойства length?
Всё дело в природе объектов. Массив — это упорядоченная коллекция с числовыми индексами. Движок JavaScript легко может сказать: «В массиве 5 элементов». А объект — это неупорядоченный набор пар «ключ-значение». Теоретически вы можете добавить свойство, удалить, снова добавить — и порядок не гарантирован (по крайней мере, в старых спецификациях).
Именно поэтому у простых объектов нет встроенного .length. Но это не значит, что мы не можем его вычислить. Давайте посмотрим, как.
Способ №1: Object.keys() — золотой стандарт
Самый популярный и рекомендуемый способ — использовать Object.keys(). Этот метод возвращает массив собственных (не унаследованных) перечисляемых свойств объекта. А у массива, как мы знаем, есть .length.
const user = {
name: 'Анна',
age: 28,
city: 'Москва',
isAdmin: false
};
const length = Object.keys(user).length;
console.log(length); // 4
Всё гениально просто! Вы получаете массив ключей, а затем берёте его длину. Этот способ работает во всех современных браузерах и Node.js.
Что входит в Object.keys()?
Важный момент: Object.keys() возвращает только собственные и перечисляемые свойства. Это значит, что:
- Собственные — не унаследованные через прототип. Если вы создали объект через класс или добавили метод в прототип, он не попадёт в результат.
- Перечисляемые — те, у которых флаг
enumerableстоит вtrue. По умолчанию все свойства, созданные через присваивание, перечисляемые. Но если вы использовалиObject.defineProperty()сenumerable: false, такое свойство будет проигнорировано.
Пример с неперичисляемым свойством:
const car = {
brand: 'Toyota',
model: 'Camry'
};
Object.defineProperty(car, 'vin', {
value: 'JT2BF22KX00000001',
enumerable: false
});
console.log(Object.keys(car).length); // 2 (vin не учитывается)
Способ №2: Object.values() и Object.entries()
Иногда вам нужно не просто количество ключей, а количество значений или пар. Здесь вам помогут два брата-близнеца:
Object.values(obj)— возвращает массив значенийObject.entries(obj)— возвращает массив массивов [ключ, значение]
Оба возвращают массивы, а значит, у них есть .length:
const book = {
title: 'Мастер и Маргарита',
author: 'Булгаков',
year: 1966
};
console.log(Object.values(book).length); // 3
console.log(Object.entries(book).length); // 3
По сути, это одно и то же количество, но Object.entries() даёт больше данных, если в дальнейшем вам понадобятся и ключи, и значения.
Способ №3: Цикл for...in (и как не попасть в ловушку)
Классический, но менее удобный способ — использовать цикл for...in и вручную считать свойства:
const person = {
name: 'Иван',
age: 30
};
let count = 0;
for (let key in person) {
if (person.hasOwnProperty(key)) {
count++;
}
}
console.log(count); // 2
Обратите внимание на hasOwnProperty(). Без этой проверки for...in пройдётся ещё и по унаследованным свойствам из прототипа. Если у объекта нет собственных методов, а есть только данные, проверка необязательна, но лучше всегда её ставить — это защита от неожиданностей.
Сравнение for...in и Object.keys()
| Критерий | for...in + hasOwnProperty | Object.keys() |
|---|---|---|
| Краткость кода | Многословно | Лаконично |
| Производительность | Чуть медленнее | Оптимизировано |
| Читаемость | Средняя | Отличная |
| Поддержка старых браузеров | IE 6+ | IE 9+ |
Вывод: используйте Object.keys() везде, где можно. for...in оставьте для случаев, когда вам нужно что-то сделать с каждым свойством, а не просто посчитать их.
Способ №4: Map — альтернатива с встроенным .size
Если вы проектируете структуру данных с нуля и знаете, что вам понадобится часто узнавать её размер, подумайте об использовании Map вместо обычного объекта. У Map есть свойство .size, которое работает мгновенно и без лишних вычислений:
const settings = new Map();
settings.set('theme', 'dark');
settings.set('fontSize', 14);
settings.set('language', 'ru');
console.log(settings.size); // 3
Плюсы Map перед объектом:
- Встроенный
.size— не нужно ничего вычислять - Ключами могут быть любые типы (не только строки)
- Сохраняет порядок вставки
- Лучше подходит для частого добавления/удаления пар
Минус: не всё можно хранить в Map. Если вам нужна сериализация в JSON или работа с API, обычный объект удобнее.
Частые ошибки при подсчёте длины объекта
Давайте разберём типичные грабли, на которые наступают разработчики.
Ошибка 1: obj.length
Самая распространённая. Просто запомните: у объектов нет .length. Если вы видите obj.length — ско