> Можно ли сравнить два объекта в JavaScript и как это сделать (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: X5Tech

Стек: JavaScript

> Пример ответа

Короткий ответ

Да, сравнить два объекта в JavaScript можно, но напрямую через == или === нельзя - они сравнивают ссылки, а не содержимое. Для сравнения по значению нужно либо вручную обойти ключи, либо использовать JSON.stringify, либо сторонние утилиты вроде lodash.isEqual. Выбор зависит от структуры объектов, наличия вложенности, методов и порядка ключей. Встроенного глубокого сравнения в языке нет.

Подробное объяснение

При сравнении объектов операторы == и === проверяют, указывают ли обе переменные на один и тот же объект в памяти. Это сравнение по ссылке. Если два объекта имеют идентичные свойства и значения, но созданы отдельно, они не равны.

Для сравнения по содержимому нужно реализовать собственную логику или использовать готовые решения. Основные подходы:

  • Поверхностное сравнение - сравниваются только собственные перечисляемые свойства первого уровня. Подходит для плоских объектов без вложенности.
  • Глубокое сравнение - рекурсивно обходит все вложенные объекты и массивы, учитывая типы значений, NaN, Date, RegExp, Map, Set и другие.
  • Сериализация - JSON.stringify превращает объект в строку, после чего строки сравниваются. Минусы: порядок ключей влияет на результат, undefined, функции и символы теряются, циклические ссылки вызывают ошибку.

Также стоит учитывать прототипы: два объекта с одинаковыми свойствами, но разными прототипами формально не равны, если сравнивать через Object.getPrototypeOf.

На практике

Для большинства реальных задач достаточно JSON.stringify, если объекты плоские и порядок ключей стабилен. Но для сложных структур лучше использовать проверенные библиотеки, например lodash.isEqual, которая покрывает edge cases: NaN, Date, регулярные выражения, Map, Set, циклические ссылки.

Если хочется без зависимостей, пишут собственную функцию глубокого сравнения. Она должна:

  • проверять ссылочное равенство первым делом;
  • сравнивать типы и конструкторы;
  • обрабатывать примитивы, включая NaN;
  • рекурсивно обходить объекты, массивы, Map, Set;
  • учитывать порядок ключей или игнорировать его (обычно игнорируют);
  • защищаться от циклических ссылок через WeakMap или Set.

Пример кода

JAVASCRIPT
function deepEqual(a, b, seen = new WeakMap()) {
// Ссылочное равенство или одинаковые примитивы
if (a === b) return true;
// Обработка NaN
if (typeof a === 'number' && typeof b === 'number' && Number.isNaN(a) && Number.isNaN(b)) {
return true;
}
// Оба должны быть объектами
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
return false;
}
// Проверка циклических ссылок
if (seen.has(a)) {
return seen.get(a) === b;
}
seen.set(a, b);
// Сравнение конструкторов
if (a.constructor !== b.constructor) return false;
// Date
if (a instanceof Date) {
return a.getTime() === b.getTime();
}
// RegExp
if (a instanceof RegExp) {
return a.source === b.source && a.flags === b.flags;
}
// Map
if (a instanceof Map) {
if (a.size !== b.size) return false;
for (const [key, value] of a) {
if (!b.has(key) || !deepEqual(value, b.get(key), seen)) return false;
}
return true;
}
// Set
if (a instanceof Set) {
if (a.size !== b.size) return false;
for (const value of a) {
if (![...b].some(v => deepEqual(value, v, seen))) return false;
}
return true;
}
// Массивы
if (Array.isArray(a)) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (!deepEqual(a[i], b[i], seen)) return false;
}
return true;
}
// Обычные объекты
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (const key of keysA) {
if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
if (!deepEqual(a[key], b[key], seen)) return false;
}
return true;
}
// Пример использования
const obj1 = { name: 'Alice', nested: { age: 30, tags: ['a', 'b'] } };
const obj2 = { name: 'Alice', nested: { age: 30, tags: ['a', 'b'] } };
console.log(deepEqual(obj1, obj2)); // true

Как отвечать на собеседовании

Начни с того, что прямое сравнение через == или === работает только по ссылке. Затем упомяни три подхода: поверхностное сравнение, глубокое сравнение и сериализацию. Объясни trade-off каждого: JSON.stringify прост, но не учитывает порядок ключей и теряет специальные типы. Глубокое сравнение - универсально, но требует аккуратности с циклическими ссылками и типами. Если используешь библиотеки, назови lodash.isEqual как стандарт де-факто. Покажи, что понимаешь edge cases: NaN, Date, Map, Set, прототипы. Если кандидат senior, ожидается, что он предложит собственную реализацию и объяснит её ограничения.

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание разницы между сравнением по ссылке и по значению;
  • знание встроенных возможностей языка и их ограничений;
  • умение проектировать рекурсивные алгоритмы;
  • внимание к деталям: типы, NaN, циклические ссылки, порядок ключей;
  • способность выбирать подходящий инструмент под задачу, а не писать велосипед там, где хватит JSON.stringify;
  • знакомство с экосистемой (например, lodash).

Типичные ошибки

  • Использование == или === для сравнения содержимого объектов - результат всегда false для разных ссылок.
  • Сравнение через JSON.stringify без учёта порядка ключей - объекты с одинаковыми данными, но разным порядком свойств будут неравны.
  • Игнорирование NaN - NaN !== NaN, поэтому прямое сравнение чисел даст false.
  • Забывают про циклические ссылки - рекурсивная функция уходит в бесконечный цикл.
  • Не учитывают специальные типы: Date, RegExp, Map, Set - сравнение через Object.keys для них не работает корректно.
  • Сравнивают только собственные свойства, забывая про унаследованные - хотя обычно это ожидаемое поведение, но стоит оговорить.
  • Не проверяют конструкторы - объект и массив с одинаковыми ключами могут быть признаны равными, хотя это разные типы.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью