Как работает приведение типов в выражениях с операторами сравнения в JavaScript
Что проверяют
Знаете ли вы алгоритм нестрогого сравнения и правила для < и >, а не набор мемов вроде [] == ![]. Следом обычно просят разобрать конкретный пример по шагам или спрашивают, почему null >= 0 истинно.
Как отвечать
Для == (алгоритм IsLooselyEqual в спецификации):
- одинаковые типы: сравнение как у
===; nullиundefined: равны друг другу и больше ничему, поэтомуnull == 0ложно;- число и строка: строка приводится к числу,
'5' == 5истинно,'' == 0тоже; - boolean: сначала сам превращается в число. Отсюда
'1' == trueистинно, а'true' == trueложно; - объект и примитив: объект проходит ToPrimitive:
Symbol.toPrimitive, затемvalueOf, затемtoString.[] == falseразворачивается так:falseв0,[]в'',''в0; - два объекта: сравниваются ссылки, приведения нет.
Для <, >, <=, >= правила свои: операнды приводятся к примитиву с подсказкой number; если оба строки, сравнение идёт по кодам UTF-16, поэтому '10' < '9' истинно. Иначе всё переводится в числа: null становится 0, undefined становится NaN, а любое сравнение с NaN ложно. >= вычисляется как «не меньше» и на == не смотрит, отсюда null >= 0 истинно при ложном null == 0.
=== приведения не делает, но и у него есть особенности: NaN === NaN ложно, +0 === -0 истинно. Точное сравнение даёт Object.is, проверку на NaN Number.isNaN.
На практике: правило ESLint eqeqeq, с опцией smart оно оставляет x == null как проверку сразу на null и undefined. TypeScript сам подсветит сравнение несовместимых типов.
Где спотыкаются
Объясняют [] == false приведением массива к булеву. Массив истинен, но == превращает в число false, а не массив в boolean.
Сравнивают даты через ==: d1 == d2 сравнивает ссылки и ложно для одинаковых моментов, хотя d1 < d2 работает через valueOf. Для равенства нужен d1.getTime() === d2.getTime().
Сравнивают значения из <input> через <: это строки, и '10' < '9' даёт true. Сначала приведите их через Number().