Menu
Русский

Приведение типов в JavaScript: неявное и явное

Как JavaScript приводит значения к нужному типу: неявные правила, из-за которых валятся проверки, явные преобразования, которым стоит отдавать предпочтение, и что когда срабатывает.

Два вида приведения типов в JavaScript

JavaScript довольно вольно обращается с типами. Если оператор получает значение «не того» типа, язык не падает с ошибкой — он просто преобразует его. Это и называется приведением типов (coercion), и бывает оно двух видов:

  • Явное приведение типов — вы сами просите о конвертации: Number("42"), String(99), Boolean(value).
  • Неявное приведение типов — его запускает сам оператор, без вашего участия: "5" - 2, "" == 0, if (value).

Под капотом и то, и другое дергает одни и те же правила преобразования. Разница только в том, кто принял решение конвертировать — вы или движок. И практически все странности JS, от которых встают волосы дыбом — "5" + 1 === "51", [] == false, null == undefined — растут именно из неявного приведения, которое срабатывает в самый неожиданный момент.

index.js
Output
Click Run to see the output here.

Запомните простую модель: когда вы пишете Number(...) или String(...) — вы чётко говорите, что вам нужно. А если нет, то у каждого оператора своё мнение о том, как преобразовать значения, — и вот именно в этих «мнениях» и прячутся баги.

Три целевых типа

Приведение типов в javascript всегда идёт к одному из трёх примитивных типов: string, number или boolean. (Есть ещё четвёртый — bigint, — но в него автоматически из других типов ничего не приводится.) Все остальные правила вытекают из того, к какому из этих типов «целится» оператор.

index.js
Output
Click Run to see the output here.

Приведение к строке — самая «добрая» операция: у каждого значения есть своё строковое представление. Обратите внимание, что объекты превращаются в бесполезное "[object Object]" — именно поэтому, когда вы выводите в консоль объект, склеенный со строкой ("user: " + user), вы почти никогда не видите того, что хотели. Вместо этого используйте JSON.stringify или шаблонные строки с конкретными полями.

Преобразование строки в число в JS

Приведение к числу работает куда строже. Строка должна действительно выглядеть как число, иначе получите NaN:

index.js
Output
Click Run to see the output here.

Из сюрпризов в скобках стоит запомнить вот что:

  • Пустая строка и строка из одних пробелов превращаются в 0, а не в NaN.
  • null становится 0, а undefined — уже NaN.
  • Пустой массив даёт 0; массив из одного элемента приводит этот самый элемент; массив из нескольких элементов превращается в NaN.

Если нужно вытащить число из строки с посторонними символами, берите parseInt или parseFloat вместо Number:

index.js
Output
Click Run to see the output here.

Всегда передавайте основание системы счисления (10) в parseInt. Без него строки, начинающиеся с "0x", будут распарсены как шестнадцатеричные, а это вряд ли то, чего вы хотели.

Приведение к булевому типу в JavaScript

Приведение к Boolean — самое простое из трёх. Есть короткий список значений, которые превращаются в false, а всё остальное становится true.

К ложным (falsy) значениям относятся:

  • false
  • 0, -0, 0n
  • "" (пустая строка)
  • null
  • undefined
  • NaN

И всё. Любое другое значение — включая "false", "0", [] и {} — считается истинным (truthy).

index.js
Output
Click Run to see the output here.

Пустые массивы и объекты часто сбивают с толку тех, кто пришёл из Python, где пустые коллекции — это false. В JavaScript всё наоборот: они truthy. Так что если нужно проверить «массив пустой?», пишите явно: arr.length === 0.

Приведение к булевому типу js происходит каждый раз, когда значение оказывается там, где ожидается boolean: в if (...), while (...), тернарнике ? : и логических операторах &&, ||, !.

Оператор + — особый случай

Большинство арифметических операторов принудительно преобразуют операнды в числа. А вот + ведёт себя иначе: если хотя бы один из операндов — строка, + склеивает строки. В остальных случаях это обычное числовое сложение.

index.js
Output
Click Run to see the output here.

Вычисление идёт слева направо. В выражении 1 + 2 + "3" сначала считается 1 + 2 = 3, а потом 3 + "3" = "33". А вот "1" + 2 + 3 сразу «уходит в строку» и остаётся там: сначала "12", потом "123".

Именно поэтому склеивать строки через + — затея ненадёжная. В шаблонных строках такой проблемы нет:

index.js
Output
Click Run to see the output here.

Шаблонная строка сначала вычисляет count + 1 как обычное числовое выражение, а уже потом подставляет результат. Никакого неявного приведения типов.

Лучше использовать явное приведение типов

Если вам действительно нужно преобразовать значение — сделайте это явно. Пара лишних символов, зато следующий читатель кода не будет гадать, что здесь происходит:

index.js
Output
Click Run to see the output here.

Та же история с булевыми значениями. !!value работает и часто встречается в коде, но Boolean(value) прямо говорит, что именно происходит:

index.js
Output
Click Run to see the output here.

Разумное правило: в прикладной логике используйте явное приведение типов, а короткие формы (+x, !!x) оставьте для тех мест, где важна лаконичность и намерение понятно из контекста.

Оператор == активно полагается на приведение типов

Операторы сравнения — главный источник сюрпризов, связанных с приведением типов. == приводит типы перед сравнением, а === — нет.

index.js
Output
Click Run to see the output here.

Каждый true выше — результат цепочки неявных преобразований из нескольких шагов, которую большинство разработчиков просто не держит в голове. И в этом вся беда: код, который работает случайно, рано или поздно сломается. Полные правила сравнения мы разберём на следующей странице, а пока запомните главное: по умолчанию используйте ===, а == оставьте только для одной идиомы — x == null (она ловит сразу и null, и undefined).

Собираем всё вместе

Разберём пример, где приведение типов выручает, а где — только мешает:

index.js
Output
Click Run to see the output here.

Обрати внимание на второй вызов. Number("") возвращает 0, а не NaN — значит, parsePrice("") вернёт 0, чего вызывающий код от этой функции вряд ли ожидает. Если пустую строку нужно отбрасывать, добавь явную проверку:

index.js
Output
Click Run to see the output here.

Знание того, какие значения приводятся к 0, а какие — к NaN, — это как раз то, что спасёт вас от неуловимого бага где-то дальше по коду.

Что запомнить

  • Приведение типов преобразует значение в строку, число или булево — в зависимости от оператора.
  • + с любой строкой превращается в конкатенацию; все остальные арифметические операторы приводят к числу.
  • Falsy-значений в JavaScript ограниченный список — выучите его наизусть. Всё остальное truthy, включая [] и {}.
  • Number("") даёт 0, Number([]) даёт 0, Number(null) даёт 0 — а вот Number(undefined) уже NaN. На таких мелочах и ловятся реальные баги.
  • Используйте явное приведение через Number(x), String(x), Boolean(x) вместо хитрых неявных трюков. Будущий вы скажет спасибо.

Дальше: операторы равенства

Всё, что приведение типов делает при сравнениях, спрятано внутри ==. На следующей странице разберём == vs === в javascript, а также Object.is, единственный случай, где == всё ещё уместен, и почему линтеры по умолчанию ругаются на нестрогую форму.

Часто задаваемые вопросы

Что такое приведение типов в JavaScript?

Это когда JavaScript сам преобразует значение из одного типа в другой — число в строку, строку в число или что угодно в булево. Неявно это происходит, когда операторы вроде +, == или if получают не тот тип, который ожидали. Явно — когда вы сами вызываете Number(x), String(x) или Boolean(x).

Чем отличается неявное приведение от явного?

Явное — это когда вы осознанно вызываете функцию преобразования: Number("42"), String(99), Boolean(value). Неявное — когда оператор сам дёргает преобразование за вашей спиной: "5" - 2 даст 3, а "5" + 2 уже "52". Явное читается понятно и предсказуемо, а неявное — главный источник багов в духе «откуда тут вообще взялся NaN».

Как преобразовать строку в число в JavaScript?

Для строгого преобразования используйте Number("42") — если строка не является корректным числом, вернётся NaN. Если нужно вытащить число из начала «грязной» строки, подойдут parseInt("42px", 10) или parseFloat("3.14em"). Унарный + (например, +"42") работает так же, как Number(), но его легко не заметить при беглом чтении кода.

Почему [] + [] возвращает пустую строку?

У оператора + нет осмысленной числовой операции для двух массивов, поэтому JavaScript приводит оба к строкам. Массив превращается в строку через соединение элементов запятыми, а пустой массив превращается в "". В итоге [] + [] становится "" + "", то есть "". Забавный трюк и хороший повод никогда не использовать + с непримитивами.

Учитесь программировать с Coddy

НАЧАТЬ