Сравнение объектов: ссылки vs значения
Примитивы (числа, строки, булевы) в JavaScript сравниваются по значению, а объекты (массивы, функции, объекты) — по ссылке: два объекта равны только если они ссылаются на один и тот же участок памяти.
Зачем нужно
Это одна из самых распространённых причин ошибок у начинающих: {} === {} даёт false, а мутация одного объекта неожиданно меняет «другой» — потому что оба ссылаются на один. Понимание этого различия критично для корректного сравнения, копирования и управления состоянием.
Где используется
- Сравнение объектов в условиях (
if,===) - Обнаружение изменений состояния в React, Vue
- Копирование объектов и массивов (shallow vs deep)
- Работа с
SetиMap(ключи объектов по ссылке)
Примитивы — по значению
let a = 5;
let b = a; // копия значения
b = 10;
console.log(a); // 5 — a не изменилась
// Сравнение
console.log(5 === 5); // true
console.log('hello' === 'hello'); // true
Объекты — по ссылке
const obj1 = { name: 'Иван' };
const obj2 = obj1; // обе переменные указывают на ОДИН объект
obj2.name = 'Анна';
console.log(obj1.name); // 'Анна' — obj1 тоже изменился!
// Сравнение
const a = { x: 1 };
const b = { x: 1 };
console.log(a === b); // false — разные объекты в памяти
console.log(a === a); // true — та же ссылка
Массивы тоже по ссылке
const arr1 = [1, 2, 3];
const arr2 = arr1;
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4] — тот же массив!
console.log([1, 2] === [1, 2]); // false — разные массивы
Как правильно сравнивать объекты
// 1. Через JSON (простые объекты без методов и undefined)
const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
deepEqual({ x: 1, y: 2 }, { x: 1, y: 2 }); // true
deepEqual([1, 2, 3], [1, 2, 3]); // true
// Ограничения: порядок ключей важен, нет поддержки Date, функций, undefined
// 2. Рекурсивная проверка
function isEqual(a, b) {
if (a === b) return true;
if (typeof a !== 'object' || typeof b !== 'object') return false;
if (a === null || b === null) return false;
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every(key => isEqual(a[key], b[key]));
}
isEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // true
Копирование объектов
const original = { name: 'Иван', address: { city: 'Москва' } };
// Поверхностная копия (shallow copy)
const shallow1 = { ...original };
const shallow2 = Object.assign({}, original);
shallow1.name = 'Анна'; // не влияет на original
console.log(original.name); // 'Иван'
shallow1.address.city = 'СПб'; // ВЛИЯЕТ! address — вложенный объект (ссылка)
console.log(original.address.city); // 'СПб'
// Глубокая копия (deep copy)
const deep1 = JSON.parse(JSON.stringify(original)); // простые объекты
const deep2 = structuredClone(original); // ES2022, работает с Date, Map, Set
deep2.address.city = 'Казань';
console.log(original.address.city); // 'СПб' — original не затронут
Set и Map: ключи объектов по ссылке
const map = new Map();
const key1 = { id: 1 };
const key2 = { id: 1 }; // другой объект
map.set(key1, 'первый');
map.set(key2, 'второй');
console.log(map.size); // 2 — разные ссылки = разные ключи
console.log(map.get(key1)); // 'первый'
// Set с объектами
const set = new Set();
set.add({ x: 1 });
set.add({ x: 1 });
console.log(set.size); // 2 — одинаковые объекты, но разные ссылки
Частые ошибки
1. Мутация объекта-аргумента
function addRole(user) {
user.role = 'admin'; // мутирует оригинал!
return user;
}
const ivan = { name: 'Иван' };
addRole(ivan);
console.log(ivan.role); // 'admin' — оригинал изменён
// Лучше возвращать новый объект:
function addRole(user) {
return { ...user, role: 'admin' };
}
2. Сравнение массивов через ===
const result = fetchData;
if (result === ) { /* никогда не выполнится */ }
// Правильно:
if (result.length === 0) {}
if (Array.isArray(result) && result.length === 0) {}
Связанные темы
- Структурированное клонирование -- structuredClone
- Приведение типов -- явное и неявное
- Структуры данных
- _MOC JavaScript