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

Что такое сравнение по значению?

1.3 Junior🔥 201 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сравнение по значению (Value Comparison) в JavaScript

Сравнение по значению — это сравнение содержимого (значений) переменных, а не их ссылок в памяти. Это фундаментальная концепция для понимания того, как JavaScript работает с разными типами данных.

Два вида сравнения в JavaScript

1. Сравнение по ссылке (Reference Comparison)

Объекты и массивы сравниваются по ссылке на место в памяти:

const obj1 = { name: 'John' };
const obj2 = { name: 'John' };

obj1 === obj2; // false — разные ссылки в памяти
obj1 == obj2;  // false — разные ссылки
obj1 === obj1; // true — одна и та же ссылка

2. Сравнение по значению (Value Comparison)

Примитивные типы сравниваются по значению:

const num1 = 5;
const num2 = 5;
num1 === num2; // true — одинаковые значения

const str1 = 'hello';
const str2 = 'hello';
str1 === str2; // true — одинаковые значения

const bool1 = true;
const bool2 = true;
bool1 === bool2; // true — одинаковые значения

Почему это происходит?

В JavaScript есть два типа данных:

Примитивные типы (Primitive Types):

  • number
  • string
  • boolean
  • undefined
  • null
  • symbol
  • bigint

Они хранят значение напрямую в памяти и сравниваются по значению:

// Память
// переменная | значение
// a          | 5
// b          | 5

const a = 5;
const b = 5;
a === b; // true

Сложные типы (Reference Types):

  • Object
  • Array
  • Function
  • Date

Они хранят ссылку на объект в памяти и сравниваются по ссылке:

// Память
// переменная | ссылка | объект
// obj1       | #001  | { name: 'John' }
// obj2       | #002  | { name: 'John' }

const obj1 = { name: 'John' }; // ссылка #001
const obj2 = { name: 'John' }; // ссылка #002
obj1 === obj2; // false — разные ссылки

Как сравнить объекты по значению?

1. JSON.stringify (простой, но с ограничениями)

const obj1 = { name: 'John', age: 30 };
const obj2 = { name: 'John', age: 30 };

JSON.stringify(obj1) === JSON.stringify(obj2); // true

// Но есть проблемы:
const obj3 = { name: 'John', age: 30 };
const obj4 = { age: 30, name: 'John' }; // другой порядок

JSON.stringify(obj3) === JSON.stringify(obj4); // false!

// И может быть очень медленно для больших объектов

2. Глубокое сравнение (Deep Comparison)

function deepEqual(obj1, obj2) {
  if (obj1 === obj2) return true;
  
  if (obj1 == null || obj2 == null) return false;
  if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
  
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  
  if (keys1.length !== keys2.length) return false;
  
  for (let key of keys1) {
    if (!keys2.includes(key)) return false;
    if (!deepEqual(obj1[key], obj2[key])) return false;
  }
  
  return true;
}

const obj1 = { user: { name: 'John', age: 30 } };
const obj2 = { user: { name: 'John', age: 30 } };

deepEqual(obj1, obj2); // true

3. Lodash _.isEqual() (рекомендуется для production)

import _ from 'lodash';

const obj1 = { name: 'John', nested: { age: 30 } };
const obj2 = { name: 'John', nested: { age: 30 } };

_.isEqual(obj1, obj2); // true

4. Object.is() для примитивов (с особенностями)

Object.is(5, 5); // true
Object.is('hello', 'hello'); // true
Object.is(NaN, NaN); // true (в отличие от NaN === NaN)
Object.is(0, -0); // false (в отличие от 0 === -0)

Практический пример: сравнение в React

// ❌ Это не работает как может показаться
function MyComponent() {
  const [user, setUser] = useState({ name: 'John' });
  
  useEffect(() => {
    console.log('User changed');
  }, [user]); // user сравнивается по ссылке!
  
  // При каждом рендере создаётся НОВЫЙ объект
  const config = { timeout: 5000 };
  
  return <Child config={config} />;
}

// ✅ Правильно
function MyComponent() {
  const [user, setUser] = useState({ name: 'John' });
  
  useEffect(() => {
    console.log('User changed');
  }, [user.name]); // сравниваем примитив
  
  // Или используем useMemo
  const config = useMemo(() => ({ timeout: 5000 }), []);
  
  return <Child config={config} />;
}

Сравнение в условиях

// Примитивы
if (count === 5) { } // по значению
if (name === 'John') { } // по значению

// Объекты
const user1 = { id: 1 };
const user2 = { id: 1 };

if (user1 === user2) { } // false — разные объекты
if (user1.id === user2.id) { } // true — одинаковые id

Особенности сравнения

1. Строгое === vs нестрогое ==

// Строгое сравнение (используй это)
5 === '5'; // false — разные типы
0 === false; // false
null === undefined; // false

// Нестрогое сравнение (избегай)
5 == '5'; // true — преобразует тип
0 == false; // true
null == undefined; // true

2. NaN — особенный случай

NaN === NaN; // false!
NaN == NaN; // false!

// Правильно проверить NaN
Number.isNaN(NaN); // true
Object.is(NaN, NaN); // true

3. Сравнение массивов

const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];

arr1 === arr2; // false — разные ссылки

// Правильное сравнение
JSON.stringify(arr1) === JSON.stringify(arr2); // true

// Или
arr1.length === arr2.length && arr1.every((val, idx) => val === arr2[idx]);

На собеседовании

Краткий ответ: В JavaScript примитивные типы (number, string, boolean) сравниваются по значению, а объекты и массивы — по ссылке в памяти. Это значит, что два одинаковых объекта будут разными при сравнении ===.

Развёрнутый ответ: JavaScript разделяет типы данных на примитивы и ссылочные типы. Примитивы хранят значение напрямую и сравниваются по значению. Объекты и массивы хранят ссылку и сравниваются по ссылке. Для сравнения объектов по значению используют JSON.stringify, глубокое сравнение, Object.is для примитивов или библиотеки типа lodash. Это очень важно в React, где нужно правильно указывать зависимости в useEffect и useMemo.