Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Чому [] == false повертає true». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Тому що нестроге порівняння `==` приводить обидві сторони до числа, і в підсумку рушій порівнює `0 == 0`.** Спочатку `false` стає числом `0`, потім масив як об'єкт приводиться до примітиву через `valueOf()` і `toString()`, що для порожнього масиву дає порожній рядок `''`, а `Number('')` це знову `0`. Зі строгим порівнянням цього не відбувається: `[] === false` завжди `false`, бо типи різні і жодного приведення немає. ```javascript [] == false; // true: false -> 0, [] -> '' -> 0, отже 0 == 0 [] === false; // false: порівняння без приведення типів ``` **Ключове:** `==` порівнює значення після приведення типів, `===` порівнює тип і значення, тому в коді варто використовувати саме `===`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`[] == 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 | `[] == false` | `false -> 0` | | 2 | `[] == 0` | `[] -> ''` | | 3 | `'' == 0` | `'' -> 0` | | 4 | `0 == 0` | `true` | ### Чому це небезпечно Ця логіка робить `==` **непередбачуваним**. Ось іще приклади того самого механізму: ```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 -> 0` | `true` | | `[] === 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 забороняє `==` саме через такі випадки, і вимикати його заради «коротшого запису» не варто.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.