Как проверить, что объект пустой в JavaScript
Что проверяют
Знание того, какие ключи видит каждый способ перебора, и умение отличить простой объект без полей от null, Map или экземпляра класса. Следом спрашивают, почему obj === {} всегда false и чем плох JSON.stringify.
Как отвечать
Базовый ответ:
const isEmpty = (obj) => Object.keys(obj).length === 0;Object.keys возвращает только собственные перечисляемые строковые ключи. Дальше покажите, где этого мало:
- символы и неперечисляемые свойства:
Object.keysих не видит. Полная проверка выглядит так:Reflect.ownKeys(obj).length === 0; nullиundefined:Object.keys(null)бросаетTypeError, поэтому сначала проверьте, что это вообще объект;- не простые объекты: у
new Map([[1, 2]]),new Set([1])иnew Date()собственных ключей нет, и функция скажет «пусто». Для коллекций смотрите.size, а простой объект отличайте проверкойObject.getPrototypeOf(obj) === Object.prototype; for...in: обходит и унаследованные свойства, поэтому внутри нуженObject.hasOwn(obj, key)из ES2022. Старыйobj.hasOwnProperty(key)упадёт на объекте изObject.create(null), у которого нет прототипа. Плюс цикла в раннем выходе: он не создаёт массив всех ключей, но заметно это только на очень больших объектах;- готовые утилиты:
isEmptyиз lodash понимает и строки, и массивы, иMap, но тянуть библиотеку ради одной проверки не стоит.
obj === {} всегда false, потому что объекты сравниваются по ссылке, а литерал создаёт новый объект. По той же причине в React объект, пересоздаваемый на каждый рендер, заставляет useEffect с ним в зависимостях срабатывать каждый раз.
Где спотыкаются
Используют JSON.stringify(obj) === '{}'. Он пропускает свойства со значением undefined, функции и символы, поэтому { a: undefined } признаётся пустым, а на циклической ссылке или BigInt вызов бросает исключение.
Не проверяют вход и падают на null, пришедшем из API вместо объекта.
Пишут for...in без проверки собственных свойств и получают «не пусто» из-за поля, добавленного в прототип.