Приведение типов в JS

Магия или простые правила?

«Где отсутствует точное знание, там действуют
догадки, а из десяти догадок девять — ошибки»
(с) Максим Горький

Данный материал, в первую очередь, вам не только поможет пройти собеседования на позицию Frontend разработчика, но вам лично прояснит как же все-таки работает магия в JS и почему. Эта статья может сделать вашу жизнь js-программиста более легкой. Для затравки вот вам задачка:

const bool = new Boolean(false);
if (bool) console.log(bool);
if (bool == false) console.log(bool);

Что выведет?

Забегая вперед, скажу что отработают оба условия, при этом везде будет вывод что-то типа Boolean{false}. Почему так? Плохой и непонятный JS? Да нет, JS очень даже хороший и понятный, просто нужно все это понять.

Вообще написание этого материала — это подготовка к одной хорошей и интересной теме, связанной с полиморфизмом и перегрузкой в JS. Но когда я писал статьи про перегрузку и полиморфизм понял, что есть много вещей, которые нужно объяснить до того, как расскажу про них иначе будет сложно. Поэтому будем идти по порядку.

Система преобразования типов в JS очень простая, хотя на первый взгляд этого и не скажешь. Она сильно отличается от других языков программирования — это факт, поэтому у многих программистов, пришедших из других языков программирования в JavaScript часто вызывает когнитивный диссонанс. Но если разобраться, то реально все просто. Нужно усвоить, что в JS есть всего 3 приведения типов:

  1. Строковое
  2. Численное
  3. Логическое

Строковое преобразование происходит при необходимости представления чего-либо в виде строки. Тут все просто.

Численное преобразование происходит в математических выражениях, а также при сравнении.

Логическое преобразование — приведение к true и false, происходит в логическом контексте(например в условиях) и при применении логических операторов. Все значения, которые можно трактовать как пустые, становятся false: 0, undefined, null, NaN, пустая строка.

Все остальное, в том числе и любые объекты — true.

В JavaScript логическое преобразование особенно интересно своим сочетанием с численным.

Дабы не писать Boolean, я заменю его на двойное преобразование !! — но оно не меняет сути и поведения.

Пример такой двусмысленности (если не знать правил):

const a = 0;
const b = ' 0 ';
console.log(!!a, !!b); // false, true
console.log(a == b); // true

Значение a в логическом контексте false, так как 0 — интуитивно пустое значение, как писалось выше. Значение b не пустое в логическом контексте, так как содержит строку из 3х символов (2 пробела и 0).

А когда мы сравниваем 0 и “ 0 ”, то мы сравниваем их не в логическом контексте, а в численном, поэтому у нас получается такое сравнение с преобразованием типов:

Number(0) == Number(" 0 ")

Таким образом нужно всего лишь запомнить, что в JavaScript есть 3 типа преобразований:

  1. String() — приведение к строке в строковом контексте (например при конкатенации строк).
  2. Number() — приведение к примитиву в численном контексте, включая унарный плюс (+value). Происходит при сравнении разных типов.
  3. Boolean() — приведение к логическому типу в логическом контексте.

Особым случаем является проверка на равенство таких специальных типов как Null и Undefined. Они равны только друг другу и неравны всему остальному. Это прописано в спецификации.

null == null
undefined == undefined
undefined == null
null == undefined

Возвращаясь к нашей задаче, которая была описана выше, так почему же так отработает код?

Если вы внимательно прочли статью, то теперь можете сами объяснить это. Давайте сверимся. Было дано:

const bool = new Boolean(false);
if (bool) console.log(bool);
if (bool == false) console.log(bool);

Мы используем класс Boolean, который создает не примитив, а объект — экземпляр класса Boolean. Для таких объектов в JS прописана своя модель обработки объектов в логическом контексте, о чем писалось выше. Если bool — это объект, то в логическом контексте, неважно что это за объект и от какого класса, он всегда будет приведен к true:

Boolean(bool) // = true
// поэтому
if (bool) // = true

И все логично и все подчиняется правилам. Никакой магии. При этом, если мы производим сравнение, то тут сработает численное сравнение, при котором будет вызван “магический” метод valueOf:

bool.valueOf() == false
// поэтому
bool == false // true
if (bool == false) // true

Про магические методы мы поговорим в следующей статье. И так, вы видите, что все логично и все подчиняется правилам. Никакой магии.

Зачем это знать?

Нет, не затем, чтобы проходить собеседования. Понимать, как устроен ваш язык программирования, как он работает и почему — это та отличительная черта настоящего профессионала программиста от ремесленника. Понимание таких особенностей, позволяет в разы сократить поиск и устранение ошибок. Позволяет не плеваться и говорить какой язык плохой, а делать вещи, которые изначально кажутся невозможными. Да банально для общего развития. Как говорил М.В. Ломоносов:

“Математику уже за то учить надо, что она ум в порядок приводит”
(с) М.В. Ломоносов

Если хотите развиваться, вам стоит понимать, как устроен ваш инструмент. В других языках программирования это норма. Норма — знать свой инструмент досконально. Но не обязательно, да.

Можно мне возразить и сказать, что можно работать и не знать ничего такого. Но, для примера, я вам могу привести следующее. У меня сейчас есть несколько вакансий, где нужны именно глубокие знания JS.

  1. Вакансия — фронтендер в RnD отдел. В задачи будет входить разработка инструментов для разработчиков, проведение различных экспериментов, создание новых инструментов вплоть до разработки своего фреймворка, если потребуется. Работа под руководством, известного в российских кругах, очень крутого фронтендера, который внес большой вклад в опенсорс и российское фронтенд сообщество. Если интересно, детали по ссылке:

2. Другой проект — тоже RnD отдел. В ваши задачи будет входить разработка с нуля большой технологической платформы для разработчиков, разработка своего собственного фреймворка и , что самое интересное, нужно написать свой интерпретатор своего собственного высокоуровневого бизнес языка (DSL), который будет работать на клиенте. Это только часть задач и тут как никогда нужны глубокие знания в JS и не только. Если заинтересовались, по ссылке детали вакансии:

И это еще не все. Эти вакансии для кого-то могут стать вакансиями мечты. И условия там просто шикарные. И у вас есть шанс попасть в эти команды и делать вещи, которые никто до вас не делал.


Кстати, следить за обновлениями и прочими материалами от меня можно в телеграм канале: @prowebit

В этом канале публикую не только статьи из этого блога, но и различные новости и мысли. Подписывайтесь ;)