← Назад к вопросам
В чем разница между Deep и Shallow сравнением объектов?
2.2 Middle🔥 171 комментариев
#JavaScript Core#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Shallow vs Deep сравнение объектов в JavaScript
Это фундаментальная концепция в JavaScript и React. Неправильное понимание приводит к bugs в comparisons, memoization и performance issues.
Основы: Reference vs Value
JavaScript различает примитивы и объекты:
// ПРИМИТИВЫ: сравниваются по значению
const a = 5;
const b = 5;
a === b; // true (одно и то же значение)
const str1 = "hello";
const str2 = "hello";
str1 === str2; // true (одно и то же значение)
// ОБЪЕКТЫ: сравниваются по ссылке (reference)
const obj1 = { name: "John" };
const obj2 = { name: "John" };
obj1 === obj2; // FALSE!! (разные объекты в памяти)
obj1 == obj2; // FALSE (тоже разные)
const obj3 = obj1;
obj1 === obj3; // TRUE (одна и та же ссылка)
Shallow Сравнение (Поверхностное)
Shallow сравнение проверяет только ПЕРВЫЙ уровень свойств:
function shallowEqual(obj1, obj2) {
// 1. Проверяем количество свойств
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
// 2. Проверяем каждое свойство (только сравнение ==)
for (let key of keys1) {
if (obj1[key] !== obj2[key]) return false;
}
return true;
}
// Примеры
const user1 = { name: "John", age: 30 };
const user2 = { name: "John", age: 30 };
shallowEqual(user1, user2); // TRUE (примитивы совпадают)
const user3 = {
name: "John",
address: { city: "Moscow" }
};
const user4 = {
name: "John",
address: { city: "Moscow" }
};
shallowEqual(user3, user4); // FALSE!!!
// Потому что user3.address !== user4.address (разные объекты)
Суть: Shallow сравнение смотрит только на первый уровень. Если свойство — объект, сравнивается только ссылка, не содержимое.
Deep Сравнение (Глубокое)
Deep сравнение рекурсивно проверяет ВСЕ уровни вложенности:
function deepEqual(obj1, obj2) {
// 1. Базовый случай: примитивы
if (obj1 === obj2) return true;
if (obj1 == null || obj2 == null) return false;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
// 2. Проверяем количество свойств
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
// 3. Рекурсивно сравниваем каждое свойство
for (let key of keys1) {
if (!deepEqual(obj1[key], obj2[key])) return false;
}
return true;
}
// Примеры
const user3 = {
name: "John",
address: { city: "Moscow" }
};
const user4 = {
name: "John",
address: { city: "Moscow" }
};
deepEqual(user3, user4); // TRUE!!! (проверены все уровни)
// Вложеннее
const complex1 = {
user: { name: "John", skills: ["React", "Node"] },
meta: { created: "2024-01-01" }
};
const complex2 = {
user: { name: "John", skills: ["React", "Node"] },
meta: { created: "2024-01-01" }
};
deepEqual(complex1, complex2); // TRUE (всё совпадает)
React и Memoization: практическое применение
React.memo (Shallow по умолчанию)
const UserCard = React.memo(({ user }) => {
return <div>{user.name}</div>;
});
// React.memo использует SHALLOW сравнение props
const App = () => {
// Вариант 1: Новый объект каждый render
<UserCard user={{ name: "John" }} /> // ПЕРЕРЕНДЕР каждый раз!
// Вариант 2: Стабильный объект
const user = { name: "John" };
<UserCard user={user} /> // memo сработает, нет перерендера
};
useMemo и useCallback
const Component = ({ deps }) => {
// Плохо: deps объект создаётся каждый раз
// useEffect сработает на каждый render
useEffect(() => {
console.log(deps);
}, [deps]);
// Хорошо: используй useMemo для deps
const memoizedDeps = useMemo(() => deps, [dep.id, dep.name]);
useEffect(() => {
console.log(memoizedDeps);
}, [memoizedDeps]); // Теперь useEffect срабатывает правильно
};
Custom comparator
const UserCard = React.memo(
({ user }) => <div>{user.name}</div>,
// Custom comparator (shallow по умолчанию)
(prevProps, nextProps) => {
// Вернуть TRUE если не перерисовывать
// Вернуть FALSE если перерисовывать
return prevProps.user.id === nextProps.user.id; // Deep сравнение ID
}
);
Практические примеры
Shallow сравнение лучше (производительность)
// Компонент список товаров
const ProductList = React.memo(({ products }) => {
// Shallow сравнение: OK
// Если products ссылка та же = нет перерендера
return <div>{products.map(p => p.name).join(', ')}</div>;
});
const App = () => {
const products = ["Apple", "Banana"]; // Стабильная ссылка
return <ProductList products={products} />
};
Deep сравнение нужно (корректность)
function areUsersEqual(user1, user2) {
// user содержит вложенные объекты (profile, address)
// Shallow не будет работать
return deepEqual(user1, user2);
}
const user1 = {
id: 1,
profile: { name: "John", age: 30 },
address: { city: "Moscow", zip: "123" }
};
Библиотеки
// Lodash
import { isEqual, isShallowEqual } from 'lodash-es';
isShallowEqual(obj1, obj2); // Shallow
isEqual(obj1, obj2); // Deep
// Reselect (Redux)
import { createSelector } from 'reselect';
const selectUser = createSelector(
[state => state.user],
user => user // Shallow сравнение по умолчанию
);
// dequal (маленькая, быстрая)
import { dequal } from 'dequal/lite';
dequal(obj1, obj2); // Deep сравнение
Производительность
const largeObject = {
user: { /* 100 свойств */ },
data: { /* 1000 элементов */ },
meta: { /* 50 свойств */ }
};
// Shallow: O(k) где k — количество свойств на первом уровне (3)
shallowEqual(largeObject, largeObject); // Быстро
// Deep: O(n) где n — ВСЕ свойства рекурсивно
deepEqual(largeObject, largeObject); // Медленно
Вывод
| Сравнение | Проверяет | Скорость | Когда использовать |
|---|---|---|---|
| Shallow | Первый уровень | Очень быстро | React.memo, props |
| Deep | Все уровни | Медленнее | Обработка данных, валидация |
| === (Reference) | Только ссылку | Молниеносно | Примитивы, ID |
Практический совет:
- По умолчанию используй Shallow — это быстрее
- Используй Deep только если действительно нужно — когда сравниваешь сложные структуры данных
- В React мемоизируй стабильные ссылки — не полагайся на deep сравнение
- Профилируй — используй DevTools, чтобы увидеть, где это проблема