Превратить объект в булевое значение — задача почти всегда переформулируется как: «есть ли в нём что-то полезное?». Хотим получить true, если объект «живой» (содержит нужные поля), и false, если он пустой или равен null.
Главная сложность: прямой Boolean(obj) для любого объекта (включая пустой {}) возвращает true. Это потому, что любой объект — truthy по правилам стандарта. Поэтому для осмысленной проверки нужно идти глубже.
Boolean({}) // true
Boolean({ a: 1 }) // true
Boolean(null) // false (null — единственное исключение среди «объектов»)
Boolean(undefined) // false
// прямой Boolean плохо отличает «есть данные» от «нет»:
let user = {};
if (user) {
console.log('зайдёт всегда, даже если объект пустой');
}
Когда это нужно
- Проверка конфига. Опциональный объект настроек: если передали — используем, если нет — берём дефолт. const options = userOptions ?? DEFAULT_OPTIONS. Но если ждём «не просто переданный, а с реальными полями внутри», нужна более глубокая проверка.
- Conditional rendering. В шаблоне Vue/React: {user && <Profile user={user}/>} — здесь хочется не рендерить пустую карточку, если у пользователя только id, но больше ничего нет.
- Feature-flag check. Объект flags с включёнными фичами: пустой — ничего не включено, заполненный — есть что показать.
- Валидация формы. Объект errors накапливает ошибки полей. Пустой — форма валидна, заполненный — нет.
- Опциональные поля API. Бэк может прислать { pricing: {} } или { pricing: null } для «ценообразования нет». Хочется одинаково обработать оба случая.
Способ 1: Object.keys + длина
Самый прямой способ — посчитать собственные ключи объекта. Object.keys(obj) возвращает массив, его .length даёт количество собственных перечисляемых свойств.
function hasContent(obj) {
if (obj == null) return false; // null или undefined → false
return Object.keys(obj).length > 0;
}
hasContent({}) // false
hasContent({ a: 1 }) // true
hasContent(null) // false
hasContent(undefined) // false
// в одну строку через ?? и optional chaining:
const isFilled = !!(obj && Object.keys(obj).length);
Запись obj == null (с двойным равно) ловит и null, и undefined — это один из редких случаев, где нестрогое сравнение читабельнее ===.
Способ 2: проверка конкретных полей
Часто «живой» объект — не «любой с хотя бы одним ключом», а «имеющий определённые поля». Например, пользователь считается осмысленным, если у него есть id и name.
function isValidUser(user) {
return user != null
&& typeof user === 'object'
&& 'id' in user
&& 'name' in user
&& user.name.length > 0;
}
isValidUser({}) // false
isValidUser({ id: 1 }) // false (нет name)
isValidUser({ id: 1, name: '' }) // false (пустое имя)
isValidUser({ id: 1, name: 'Сергей' }) // true
Оператор in проверяет наличие свойства (даже если оно undefined). Альтернатива — user.id !== undefined, но in точнее: он отличает «поля нет» от «поле есть, но undefined».
Способ 3: Object.values + some
Если объект может содержать пустые значения, но «живым» считается, только когда хотя бы одно — не пустое:
function hasAnyValue(obj) {
if (obj == null) return false;
return Object.values(obj).some(v => v != null && v !== '');
}
hasAnyValue({}) // false
hasAnyValue({ a: null, b: undefined }) // false
hasAnyValue({ a: null, b: 'данные' }) // true
hasAnyValue({ a: 0 }) // true (ноль — это значение)
Это та же логика, что и для массивов: смотрим не «есть ли структурно что-то», а «есть ли осмысленные данные».
Подводные камни
typeof null === "object". Главная ловушка. null сообщает свой тип как "object" — это историческая ошибка стандарта. Если в коде есть проверка typeof x === "object", она «поймает» и null, что обычно нежелательно. Всегда добавляй x !== null:
function isObject(x) {
return x !== null && typeof x === 'object';
}
isObject(null) // false ✓
isObject({}) // true
isObject([]) // true (массив — тоже объект)
Массив — это тоже объект. typeof [1, 2] === "object", и Object.keys([1, 2]) вернёт ["0", "1"] (индексы как строки). Если функция должна работать только с обычными объектами, исключай массивы через !Array.isArray(x).
Object.keys видит только собственные ключи. Если объект создан через Object.create(parent) — унаследованные поля не попадут в Object.keys. Для проверки всех (включая прототип) используется Reflect.ownKeys(obj) или ручной обход через for...in.
hasOwnProperty и Object.create(null). Объект, созданный через Object.create(null), не имеет .hasOwnProperty в прототипе — попытка вызова упадёт. Безопасный способ: Object.prototype.hasOwnProperty.call(obj, key) или современный Object.hasOwn(obj, key) (ES2022).
Symbol-ключи невидимы для Object.keys. Если объект содержит только Symbol-ключи (редкий случай, но возможный в библиотеках), Object.keys(obj).length вернёт ноль, даже если поля есть. Для полного списка используется Reflect.ownKeys(obj), который видит и строковые, и Symbol-ключи.
Прокси-объекты могут лгать. Proxy позволяет перехватить любое чтение. Объект new Proxy(target, handler) на Object.keys может вернуть [], даже если в реальном target что-то есть. Это очень редкий случай в обычном коде, но в библиотеках вроде Vue (реактивные объекты были прокси в Vue 3) встречается.