Skip to main content

Чому [] == false повертає true

[] == false дає true тому, що під час нестрогого порівняння JavaScript автоматично приводить обидві сторони до числа і врешті порівнює 0 == 0. Масив по дорозі перетворюється на порожній рядок, а порожній рядок на нуль, тож рівність виявляється правдивою, хоча жодної змістовної рівності тут немає.

Теорія

TL;DR

  • == запускає приведення типів (type coercion), === не запускає.
  • Булеве значення в порівнянні == завжди спершу стає числом: false це 0.
  • Об'єкт (а масив теж об'єкт) приводиться до примітиву через valueOf(), потім toString().
  • [].toString() дає порожній рядок '', а Number('') дає 0.
  • Тому вираз зводиться до 0 == 0, тобто true.
  • [] === false дає false, бо строге порівняння не робить приведення.

Швидкий приклад

javascript
console.log([] == false); // true console.log([] === false); // false // Той самий ланцюжок, розписаний уручну: console.log(Number(false)); // 0 console.log([].toString()); // '' console.log(Number('')); // 0 console.log(0 == 0); // true

Покрокове пояснення

Візьмімо вираз:

javascript
[] == false

Крок 1. == запускає type coercion. При нестрогому порівнянні JS намагається привести типи так, щоб порівняння стало можливим. Алгоритм абстрактної рівності в специфікації ECMAScript описує це правилом:

Якщо один з операндів має тип boolean, він приводиться до числа.

Крок 2. Приводимо false до числа.

javascript
false -> 0

Тепер вираз виглядає так:

javascript
[] == 0

Крок 3. Ліворуч об'єкт (масив), праворуч примітив. Якщо один з операндів об'єкт, він спершу приводиться до примітиву (через valueOf(), а потім toString()). Для масиву []:

javascript
[].toString(); // ''

Отже:

javascript
[] -> '' // порожній рядок

Тепер маємо:

javascript
'' == 0

Крок 4. Порівняння '' == 0. За тим самим алгоритмом:

Якщо один операнд рядок, а другий число, то рядок перетворюється на число.

javascript
Number('') -> 0

Отже:

javascript
0 == 0

Результат:

javascript
true

Підсумок по кроках

ЕтапВиразЩо сталося
1[] == falsefalse -> 0
2[] == 0[] -> ''
3'' == 0'' -> 0
40 == 0true

Чому це небезпечно

Ця логіка робить == непередбачуваним. Ось іще приклади того самого механізму:

javascript
[] == ![] // true '' == 0 // true '0' == 0 // true null == undefined // true [] == '' // true [0] == 0 // true [1] == 1 // true [2] == 2 // true

Особливо показовий перший рядок: ![] це false (масив завжди truthy, тому його заперечення дає false), далі працює вже знайомий ланцюжок і [] == ![] теж стає true. Тобто значення дорівнює власному запереченню, і це цілком легальний JavaScript.

Як уникнути плутанини

Використовуйте строге порівняння ===, яке не виконує неявного приведення типів.

javascript
[] === false // false '' === 0 // false null === undefined // false

Коротке зведення:

ВиразЩо робить JSРезультат
[] == false[] -> '' -> 0, false -> 0true
[] === falseПорівнює без приведенняfalse

Якщо треба перевірити, чи масив порожній, перевіряйте це явно:

javascript
if (items.length === 0) { // порожній масив }

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

  • Перевіряти порожність масиву через порівняння з false або 0. Працює випадково і ламається, щойно значення стане null або об'єктом. Перевіряйте items.length === 0.
  • Вважати, що [] == false означає «масив хибний». Насправді сам по собі [] truthy: у if ([]) тіло виконається. Хибність тут виникає лише через приведення до числа в операторі ==.
  • Плутати == з === у перевірках на null. Тут == іноді корисний: x == null ловить і null, і undefined. Але це єдиний виняток, який варто застосовувати свідомо.
  • Покладатися на valueOf() для масивів. Для звичайного масиву valueOf() повертає сам масив, тобто не примітив, тому рушій переходить до toString(). Якщо перевизначити valueOf(), результат порівняння зміниться.
  • Ігнорувати лінтер. Правило eqeqeq у ESLint забороняє == саме через такі випадки, і вимикати його заради «коротшого запису» не варто.

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

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

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