В чем разница между двойным (==) и тройным (===) равенством в JavaScript?
Что проверяют
Знаете ли вы, что == не «сравнивает только значения», а запускает конкретный алгоритм приведения типов, и можете ли предсказать его результат. Следом обычно дают пару примеров вроде [] == false или null == 0 и спрашивают, где == всё-таки уместен.
Как отвечать
===(строгое равенство): типы разные, значит сразуfalse, без преобразований. Объекты, массивы и функции сравниваются по ссылке, поэтому{} === {}даётfalse;==(нестрогое): при разных типах операнды приводятся по правилам спецификации. Строка с числом сравнивается как числа,'5' == 5истинно. Boolean сначала превращается в число, поэтому'1' == trueистинно, а'2' == trueложно. Объект приводится к примитиву черезvalueOfиtoString, отсюда[] == falseи[] == ''оба даютtrue;nullиundefined: по==равны друг другу и больше ничему.null == 0даётfalse, хотяnull >= 0даётtrue: операторы сравнения приводят к числу по другим правилам.
Упомяните два краевых случая строгого равенства: NaN === NaN ложно (проверяют через Number.isNaN), а +0 === -0 истинно. Различить и то и другое умеет Object.is.
Практический вывод: по умолчанию в коде ===, и это закрепляют правилом ESLint eqeqeq. Единственная идиома с ==, которую многие команды разрешают, это value == null как проверка на null и undefined сразу, для неё у правила есть опции "smart" и { "null": "ignore" }. В TypeScript часть проблем ловит компилятор: сравнение значений несовместимых типов он помечает ошибкой.
Где спотыкаются
Говорят, что === «сравнивает тип и значение», а == «только значение». == тоже сравнивает значения, но после приведения, и именно правила приведения интервьюер хочет услышать.
Считают, что === сравнивает объекты по содержимому. Для глубокого сравнения нужен отдельный код, а трюк с JSON.stringify ломается на порядке ключей, undefined и датах.
Путают null == 0 с null == undefined или забывают про NaN. Например, arr.indexOf(NaN) вернёт -1, а arr.includes(NaN) элемент найдёт, потому что использует другой алгоритм сравнения.