Почему одинаковые объекты дают false в js
Почему в JavaScript два объекта с одинаковым содержимым при сравнении возвращают false?
Ключевая теория: равенство в JavaScript
В JavaScript значения делятся на примитивы (например, number, string, boolean, null, undefined, bigint, symbol) и объекты (в том числе массивы, функции, даты).
Для примитивов сравнение === обычно означает “одно и то же значение”, а для объектов === означает “одна и та же ссылка (тот же самый объект в памяти)”.
===; для этого нужны отдельные алгоритмы “глубокого сравнения”.Почему два одинаковых литерала не равны
При записи двух литералов создаются два разных экземпляра, даже если поля и значения выглядят одинаково:
const a = { x: 1, y: 2 };
const b = { x: 1, y: 2 };
console.log(a === b); // false
console.log(a == b); // false
Это происходит потому, что сравнение объектов с помощью === возвращает true только если оба операнда указывают на один и тот же объект.
То есть сравнивается “идентичность” (reference identity), а не структура.
Для контраста: одна и та же ссылка сравнивается как равная самой себе:
const a = { x: 1 };
const alias = a;
console.log(a === alias); // true
Разбор вариантов 1–4
| Вариант | Верно | Почему |
|---|---|---|
| 1 | Нет | === для объектов не “всегда false”: при сравнении одной и той же ссылки получается true (пример с alias). |
| 2 | Нет | JavaScript не делает объекты “разными” из‑за сортировки ключей при создании; даже при одинаковом порядке ключей два литерала остаются разными экземплярами, и === всё равно даст false. |
| 3 | Нет | === не сравнивает “количество свойств”; для объектов он проверяет идентичность ссылки, поэтому результат предсказуем: либо один и тот же объект (true), либо разные (false). |
| 4 | Да | === для объектов — это сравнение ссылок; два одинаковых литерала создают два объекта → false. |
Как сравнивать объекты “по содержимому”
Поверхностное сравнение (shallow)
Подходит, если объект плоский (значения — примитивы) и требуется сравнить только верхний уровень:
function shallowEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (typeof a !== "object" || typeof b !== "object") return false;
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) return false;
for (const k of aKeys) {
if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
if (a[k] !== b[k]) return false;
}
return true;
}
console.log(shallowEqual({x: 1, y: 2}, {x: 1, y: 2})); // true
console.log(shallowEqual({x: {z: 1}}, {x: {z: 1}})); // false (вложенный объект сравнивается по ссылке)
Глубокое сравнение (deep)
Нужно, если есть вложенные объекты/массивы:
function deepEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (typeof a !== "object" || typeof b !== "object") return false;
// массивы и обычные объекты обрабатываются отдельно
const aIsArray = Array.isArray(a);
const bIsArray = Array.isArray(b);
if (aIsArray !== bIsArray) return false;
if (aIsArray) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (!deepEqual(a[i], b[i])) return false;
}
return true;
}
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) return false;
for (const k of aKeys) {
if (!Object.prototype.hasOwnProperty.call(b, k)) return false;
if (!deepEqual(a[k], b[k])) return false;
}
return true;
}
console.log(deepEqual({x: {z: 1}}, {x: {z: 1}})); // true
console.log(deepEqual([1, [2, 3]], [1, [2, 3]])); // true
Date, Map, Set, RegExp, циклические ссылки, разные прототипы, NaN, функции. Поэтому часто применяются готовые библиотеки или явно задаётся правило сравнения под конкретную задачу.Сравнение через JSON (когда допустимо)
Иногда используется при простых данных (без функций, undefined, циклов), но есть ограничения:
const a = { a: 1, b: 2 };
const b = { b: 2, a: 1 };
console.log(JSON.stringify(a) === JSON.stringify(b)); // может быть false из-за порядка ключей
JSON.stringify не сериализует функции и пропускает некоторые значения (например, undefined в объектах), поэтому “равенство строк JSON” не является универсальной проверкой равенства объектов.Схема: что сравнивает ===
Переменная a ──► [ объект #1: {x:1, y:2} ]
Переменная b ──► [ объект #2: {x:1, y:2} ]
a === b → сравнение ссылок:
(ссылка на #1) === (ссылка на #2) → false
[] === [] → false, (() => {}) === (() => {}) → false.Итого: оператор === для объектов проверяет идентичность ссылки, поэтому два отдельно созданных литерала не равны; правильный вариант ответа — 4, а для сравнения “по содержимому” требуется отдельный алгоритм (поверхностный или глубокий) либо библиотека.