Skip to main content

Слабка (==) проти суворої (===) рівності в JavaScript

Сувора рівність (===) перевіряє значення і тип без жодних перетворень. Слабка рівність (==) спочатку перетворює операнди до спільного типу, а потім порівнює результати.

Теорія

TL;DR

  • === — як підібрати взуття точно за розміром і кольором; == — як розтягнути його під будь-яку ногу
  • === не виконує жодних перетворень типів; == запускає алгоритм Abstract Equality Comparison
  • 5 === "5" дає false; 5 == "5" дає true (рядок стає числом)
  • null == undefined дає true за специфікацією; null === undefined — false
  • Правило: скрізь використовуй ===; == прийнятна лише для перевірки null/undefined

Короткий приклад

javascript
console.log(5 === 5); // true console.log(5 === "5"); // false - number vs string, без конвертації console.log(5 == "5"); // true - "5" перетворюється на 5 console.log(true === 1); // false - boolean vs number console.log(true == 1); // true - true перетворюється на 1 console.log(null == undefined); // true - спеціальне правило специфікації console.log(null === undefined);// false - різні типи

=== зупиняється на перевірці типу. == продовжує і конвертує.

Головна різниця

=== працює за алгоритмом SameValue: спочатку порівнює теги типів, потім значення. Без перетворень, без сюрпризів. == запускає Abstract Equality Comparison, який перетворює рядки на числа, булеві значення на числа (true в 1, false в 0) і викликає valueOf/toString для об'єктів. Саме через цей ланцюжок [] == 0 дає true: [] викликає toString() і отримує "", а потім "" перетворюється на 0 через ToNumber.

Коли що використовувати

  • Будь-яке звичайне порівняння: використовуй ===
  • Перевірка на null або undefined одночасно: value == null ловить обидва варіанти одним рядком
  • Порівняння введення користувача з числом після parseInt: явно через ===
  • Застарілий код, де приведення типів задокументоване навмисно: == з коментарем

Таблиця порівняння

Аспект=== (Сувора)== (Слабка)
Перевірка типуТак, тип + значенняНі, спочатку конвертує
Приведення типівВідсутнєАвтоматичне (рядок в число тощо)
ПередбачуваністьВисокаНизька
ПродуктивністьТрохи швидшеТрохи повільніше
5 == "5"falsetrue
null == undefinedfalsetrue
Коли використовуватиВсі порівнянняТільки value == null

Як це працює всередині

V8 реалізує === як пряме порівняння тегів типів із подальшою перевіркою значень. Ніяких алокацій, ніяких перетворень. Для == рушій запускає ToPrimitive для об'єктів (спочатку valueOf, потім toString), а потім ToNumber для рядків. Ланцюжок перетворень може складатися з трьох кроків перед тим, як отримати фінальний результат.

Типові помилки

Використання == там, де очікується один конкретний тип:

javascript
// Неправильно - "0" і [] теж відповідатимуть умові if (userInput == 0) { ... } // Правильно if (userInput === 0) { ... }

Перевірка порожнього масиву через ==:

javascript
// Неправильно - ніколи не буде true, посилання різні if (data == []) processData(); // Правильно if (data.length === 0) processData();

NaN ніколи не дорівнює самому собі:

javascript
console.log(NaN === NaN); // false console.log(NaN == NaN); // false - те саме правило // Правильна перевірка Number.isNaN(value);

Умова циклу зі строковим введенням:

javascript
// Неправильно - рядок "1" відповідає умові, цикл виконується несподівано while (count == 1) count--; // Правильно while (count === 1) count--;

Де зустрічається в реальному коді

  • React: props.id === expectedId у захисті компонентів
  • Express: req.params.id === 'new' в обробниках маршрутів
  • Node.js: process.env.NODE_ENV === 'production'
  • Redux: action.type === types.FETCH_SUCCESS
  • Правило ESLint eqeqeq автоматично примушує використовувати === у кодовій базі

Питання на співбесіді

Q: Чому [] == 0 дає true?
A: [] викликає toString() і отримує "", потім "" перетворюється на 0 через ToNumber. Тому [] == "" і "" == 0, звідси [] == 0 — true.

Q: null == undefined — це true? Чому?
A: Так, це спеціальний випадок в Abstract Equality Comparison за специфікацією. Тільки null і undefined дорівнюють одне одному через ==; жодне з них не дорівнює 0, false чи "".

Q: NaN === NaN — false. Як перевірити на NaN?
A: Використовуй Number.isNaN(value). Трюк x !== x теж спрацює, бо NaN — єдине значення, що не дорівнює самому собі. Object.is(value, NaN) також варіант.

Q: Чи є місце для == у продакшені?
A: Один випадок: if (value == null) ловить і null, і undefined одним рядком. Деякі команди свідомо використовують цей патерн. В усьому іншому === — безпечний вибір за замовчуванням.

Q: Що додає Object.is() порівняно з ===?
A: Два граничних випадки: Object.is(NaN, NaN) дає true (на відміну від ===), і Object.is(+0, -0) дає false (на відміну від ===). Для більшості коду === достатньо, але Object.is точно відповідає специфікації SameValue.

Приклади

Базова поведінка приведення типів

javascript
// Сувора рівність - типи повинні збігатися console.log(5 === 5); // true console.log(5 === "5"); // false console.log(false === 0); // false // Слабка рівність - приведення типів у дії console.log(5 == "5"); // true - рядок стає числом console.log(false == 0); // true - false стає 0 console.log("" == 0); // true - порожній рядок стає 0 console.log("0" == false); // true - обидва стають 0

== порівнює без конвертації лише тоді, коли обидві сторони вже мають однаковий тип.

Патерн у React-компоненті

javascript
function UserCard({ userId, isActive }) { // Сувора перевірка - "0" не сприймається як відсутній пропс if (userId === undefined) return <div>No user</div>; // Безпечна перевірка через == - ловить і null, і undefined if (isActive == null) return <div>Status unknown</div>; return <div>User {userId} is {isActive ? "active" : "inactive"}</div>; } // Патерн з process.env - завжди сувора рівність const isProd = process.env.NODE_ENV === "production";

Я сам почав суворо дотримуватись === після того, як витратив час на відлагодження циклу, який виконувався зайвий раз: API повертав рядок "1" замість числа 1. Без === це не видно одразу.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?