Добро пожаловать в мир безумия JavaScript
Если вы знакомы с JavaScript, вы, скорее всего, столкнулись с несколькими неожиданными и странными проявлениями поведения при работе с ним. Некоторые из них могут стать хорошей шуткой между разработчиками, но знание их может сэкономить вам много времени и сэкономить проблемы при отладке кода.
В этой статье я хочу познакомить вас с некоторыми из этих причуд.
1. Равенство и одинаковость
Есть тонкие, но важные различия в том, как JavaScript определяет равенство и сходство. Для новичков, вероятно, наиболее запутанной является разница между абстрактной ==
и строгой ===
проверкой равенства.
Для начала рассмотрим несколько примеров:
Как насчет сравнения с null
?
Если преобразовать null
в логическое значение, получится false
.
Однако, если вы попытаетесь сравнить null
с false
, результат все равно будет false
!
Что, если мы сравним другие ложные значения, такие как 0
или ""
, с false
?
Тогда вы можете предположить, что это тоже будет false
. Это справедливо только для строгой проверки на равенство, абстрактное выражение приведет к true
!
В приведенном выше примере мы видели, что проверка абстрактного равенства в некоторых случаях классифицирует две переменные разных типов как равные. Однако, если вы сравните пустой массив, который является истинным значением, с true
, вы снова можете удивиться:
Важно знать разницу между сравнением абстрактного и строгого равенства в JavaScript. Я настоятельно рекомендую прочитать эту статью от MDN, где они подробно объясняют равенство и сходство.
2. Математическое безумие
Продолжаем путь к математике! В JavaScript есть несколько замечательных особенностей:
Почему первый пример верен, а второй нет? Причина в том, что первый работает только в этом конкретном случае. Если вы посмотрите на порядок, в котором JavaScript оценивает эти выражения, становится ясно, почему вы получаете эти результаты:
Еще одна ловушка, когда дело доходит до математики в JavaScript, - это «магически» возрастающие числа:
Из-за стандарта IEEE 754 –2008 JavaScript округляет в этом масштабе до следующего ближайшего четного числа. То же самое и для многих других языков программирования, не только для JavaScript. Стандарт также отвечает за следующую проблему:
Проблема подробно объясняется в этом ответе на StackOverflow.
Также важно понимать, как JavaScript выбирает между сложением и объединением. В общем, можно сказать, что каждый раз, когда дополнительно задействуется строка, в какой-то момент операции она становится конкатенацией.
Однако есть несколько удивительных случаев, когда добавление становится совершенно другим типом, как вы можете видеть в примере ниже:
Подождите, я оставил самый запутанный пример на конец. Давайте сравним результаты функций Math.min()
и Math.max()
Это определенно не то, что я ожидал, когда впервые использовал эту функцию. Чарли Харви объяснил такое поведение в своем блоге, так что загляните туда, если вас интересуют подробности.
3. Развлечение с массивами
Мы уже видели несколько примеров с массивами в предыдущих разделах, но есть еще много интересных фактов о массивах.
Например, если вы попытаетесь сложить два массива вместе, вы получите следующее:
Поначалу это может сбивать с толку, но становится понятным, если вы понимаете порядок, в котором выполняется эта конкатенация.
Также интересно, как JavaScript работает с конечными запятыми.
В массивах конечные запятые будут игнорироваться, как вы можете видеть в этом примере:
Но что, если у вас несколько конечных запятых? Вот как MDN описывает поведение JavaScript для этого примера:
Если используется более одной запятой, создается пробел (или дыра). Массив с отверстиями называется разреженным (плотный массив не имеет отверстий). При итерации массивов, например, с
Array.prototype.forEach()
илиArray.prototype.map()
, дыры в массиве пропускаются.
Вы можете видеть, что первые две запятые образуют ранее описанные дыры. Однако последняя запятая будет проигнорирована, как и в случае с обычными элементами массива. Вот как мы получаем окончательную длину 5
.
4. Сложные стрелочные функции
Представленные в ES6 стрелочные функции быстро стали популярными из-за их минимального синтаксиса и более интуитивного поведения объекта this
.
Распространенная ошибка, с которой уже сталкивались почти все, кто работал со стрелочными функциями, - это когда вы возвращаете значение в своей функции:
Здесь важно не пропустить фигурные скобки; в противном случае ваша функция вернет undefined
, потому что вы открыли функцию, не вернув никакого значения.
Чтобы узнать больше о стрелочных функциях в JavaScript, вы можете взглянуть на документацию MDN.
5. Струны - это не струны?
Да, вы правильно прочитали. Взгляните на следующий пример:
Вы можете видеть, что typeof
работает, как ожидалось, но instanceof
возвращает false
для String
, хотя мы знаем, что "str"
действительно является строкой.
Однако, если вы воспользуетесь конструктором String()
, вы получите ожидаемый результат:
Что является причиной этого? MDN описывает instanceof
так:
Оператор
instanceof
проверяет, появляется ли свойствоprototype
конструктора где-нибудь в цепочке прототипов объекта. Возвращаемое значение - логическое значение.
Причина этого в том, что строковый примитив не совсем то же самое, что объект String. Если вы все еще хотите ввести проверку примитива, вам следует использовать typeof
вместо instanceof
.
6. Бонус: HTML в JavaScript!
Знаете ли вы, что вы можете написать немного HTML на JavaScript? Как вы можете видеть в следующем примере, HTML-комментарии в JavaScript вполне допустимы.
Это было введено очень давно, поэтому старые браузеры, которые еще не понимали тег <script>
, не давали сбой. Эти браузеры давно умерли, но функция осталась. Даже в NodeJS!
Это был лишь крошечный набор из многих особенностей JavaScript. Если вы хотите погрузиться глубже, я могу порекомендовать этот замечательный репозиторий GitHub от denysdovhan под названием wtfjs.