← Назад к вопросам

В чем разница между 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

Практический совет:

  1. По умолчанию используй Shallow — это быстрее
  2. Используй Deep только если действительно нужно — когда сравниваешь сложные структуры данных
  3. В React мемоизируй стабильные ссылки — не полагайся на deep сравнение
  4. Профилируй — используй DevTools, чтобы увидеть, где это проблема
В чем разница между Deep и Shallow сравнением объектов? | PrepBro