W3docs

Преобразование типов в JavaScript

Узнайте, как JavaScript преобразует значения между string, числами и boolean — явные преобразования через String(), Number(), Boolean(), неявное приведение типов и распространённые ловушки.

JavaScript — динамически типизированный язык: переменная может хранить string в один момент и число в следующий, и вам никогда не нужно заранее объявлять тип значения. Из-за такой гибкости значения постоянно перемещаются между типами — число превращается в текст для вывода, текст из поля формы становится числом для вычислений, object проверяется на «истинность» внутри if. Эти преобразования происходят двумя способами: явно, когда вы сами вызываете функцию вроде String() или Number(), и неявно (также называемое приведением типов), когда язык автоматически преобразует значение, чтобы оно подходило для операции. В этой главе рассмотрены оба способа, точные правила для каждого целевого типа и неожиданности, которые рано или поздно застают врасплох почти каждого.

Если вы ещё не читали о доступных типах данных, полезно сначала бегло ознакомиться с ними — преобразования имеют смысл только тогда, когда вы знаете, между чем именно происходит конвертация.

Три вида преобразований

Почти каждое преобразование в JavaScript нацелено на один из трёх примитивных типов: string, число или boolean. Общей операции «преобразовать в тип X» не существует; вместо этого у каждого целевого типа есть собственные правила. Мы рассмотрим каждый по очереди, а затем разберём, когда язык применяет их за вас.

Преобразование в string

Преобразование в string превращает значение в его текстовую форму. Явно это делается с помощью функции String(), а неявно — когда значение встречается со string при конкатенации или интерполируется в шаблонный литерал.

String(123);        // "123"
String(true);       // "true"
String(null);       // "null"
String(undefined);  // "undefined"
`Value: ${42}`;     // "Value: 42"

Для примитивов правила интуитивны — значение отображается именно так, как вы ожидаете его прочитать. Обратите внимание, что null и undefined становятся буквальным текстом "null" и "undefined", а не пустой строкой.

С объектами и массивами всё интереснее. Array превращается в свои элементы, соединённые запятыми, а обычный object превращается в пресловутый "[object Object]":

javascript— editable

Таким образом, пустой array при конвертации в string даёт пустую строку, а null/undefined внутри array отображаются как пустые слоты (поэтому [null, 5] даёт ",5"). Результат "[object Object]" для объектов — вот почему вы используете JSON.stringify(obj), когда действительно хотите прочитать содержимое объекта.

Числовое преобразование

Числовое преобразование превращает значение в число. Явная форма — функция Number(); неявно это происходит при унарном операторе плюс (+value) и с большинством арифметических и операторов сравнения operators.

Правила стоит запомнить, потому что они не всегда очевидны:

ЗначениеNumber(value)
undefinedNaN
null0
true / false1 / 0
"" (пустая строка)0
" 42 " (пробелы вокруг цифр)42
"42px" (нечисловое содержимое)NaN
"3.14"3.14

Некоторые из них печально известны. Number(null) равно 0, но Number(undefined) равно NaN — два «пустых» значения ведут себя по-разному. Пустая строка тоже становится 0, а не NaN. Строки сначала обрезаются от окружающих пробелов, затем строго разбираются; если остаётся что-то нечисловое, результатом будет NaN (что означает «Not a Number», то есть «не число»).

javascript— editable

Унарный плюс

Унарный оператор плюс — самый короткий способ принудительно выполнить числовое преобразование. Поставленный перед значением, +value делает ровно то же самое, что и Number(value):

+"42";       // 42
+"";         // 0
+true;       // 1
+"abc";      // NaN

Это удобно, но легко может быть неправильно прочитано, поэтому используйте его там, где намерение очевидно (например, +event.target.value при чтении числового поля формы).

parseInt и parseFloat против Number

Number() строг: вся строка должна быть допустимым числом, иначе вы получите NaN. Функции parseInt() и parseFloat() более снисходительны — они читают строку с начала и останавливаются на первом символе, который не подходит, возвращая то, что успели разобрать.

javascript— editable

Два предостережения относительно parseInt:

  • Он возвращает NaN только если строка не начинается с разбираемого числа — "px12" не работает, но "12px" даёт 12.
  • Всегда передавайте основание (base) вторым аргументом, чтобы избежать сюрпризов: parseInt("08", 10) надёжно даёт 8. Без него современные движки по умолчанию используют основание 10, но явное указание устраняет любые сомнения и документирует ваше намерение.
parseInt("FF", 16);  // 255  (hexadecimal)
parseInt("101", 2);  // 5    (binary)
parseInt("08", 10);  // 8    (decimal, explicit radix)

Используйте Number() (или +), когда хотите проверить, что значение целиком является числом, и parseInt/parseFloat — когда намеренно хотите извлечь число из начала более длинной строки, например "24px" из CSS.

Преобразование в boolean

Преобразование в boolean отвечает на единственный вопрос: является ли это значение «истинным» (truthy) или «ложным» (falsy)? Явно это делается с помощью Boolean(), а неявно — везде, где вычисляется условие: внутри if, while, тернарного ? и логических операторов.

Правило освежающе простое, потому что список falsy-значений короткий и фиксированный. Эти восемь значений преобразуются в false:

false
0
-0
0n        // BigInt zero
""        // empty string
null
undefined
NaN

Всё остальное является truthy. Это «всё остальное» включает некоторые значения, которые удивляют новичков:

javascript— editable

String "0" является truthy, потому что это непустая строка — её содержимое не имеет значения. Аналогично "false" — это вполне обычная непустая строка, и она преобразуется в true. А пустой array [] или пустой object {} являются truthy, даже если «кажутся» пустыми. Запомните список falsy-значений; если значения нет в нём, оно truthy.

Информация

Распространённая идиома для явного boolean — двойное отрицание: !!value. Первый ! преобразует в отрицательный boolean, второй переворачивает его обратно, давая тот же результат, что и Boolean(value), но короче. Например, !!"hello" равно true, а !!0 равно false.

Неявное приведение типов

До сих пор мы сами вызывали функции преобразования. Однако большинство реальных преобразований неявные — язык приводит значения, чтобы операция работала. Главное — знать, какой целевой тип ожидает каждый контекст.

Арифметика и особенность +

Каждый арифметический оператор приводит свои операнды к числам — за исключением +, который перегружен. Если хоть один операнд + является строкой, + становится конкатенацией строк; иначе он складывает числа. Все остальные операторы (-, *, /, %) всегда конвертируют в числа.

javascript— editable

Строка 1 + 2 + '3' показывает, почему важен порядок: + является лево-ассоциативным, поэтому 1 + 2 выполняется первым как числа (3), затем 3 + '3' встречает строку и конкатенируется в "33". Это единственное правило — «+ с любой строкой конкатенирует, всё остальное конвертируется в число» — объясняет значительную часть путаницы у новичков.

Условия и логика

В любом boolean-контексте — if, while, ?: и логические операторы && / || — управляющее значение прогоняется через преобразование в boolean с использованием вышеуказанного списка falsy-значений.

if ("0") {
  // runs: "0" is a non-empty string, therefore truthy
}

if (0 || "fallback") {
  // "fallback" is returned: 0 is falsy, so || moves to the next value
}

Сравнения

Операторы равенства выполняют собственное приведение типов, а оператор нестрогого равенства == в частности имеет настолько тонкие правила, что заслуживает отдельной главы. Кратко: отдавайте предпочтение строгому ===, который никогда не приводит типы. Полный набор неожиданностей (например, [] == ![] даёт true) рассмотрен в разделе операторы сравнения.

Преобразование object в примитив

Когда object используется там, где ожидается примитив (конкатенация, арифметика, сообщение alert), JavaScript сначала преобразует его в примитив. Для этого вызывается метод Symbol.toPrimitive объекта, если он существует; иначе используются valueOf() и toString(), порядок вызова которых зависит от того, ожидается ли число или строка. Именно поэтому обычный object превращается в "[object Object]", а array — в строку, соединённую запятыми — это поведение приходит из стандартного toString(). Вы можете настроить результат, определив эти методы на своих объектах, но для повседневного кода достаточно знать, что такое преобразование существует, и использовать JSON.stringify(), когда нужно читаемое представление объекта.

Распространённые ловушки

Несколько результатов преобразований порождают большинство моментов «подождите, что?». Держите их в уме:

javascript— editable

Разберём странные случаи:

  • +[] равно 0. Числовое преобразование сначала превращает array в string (""), затем пустая строка конвертируется в 0.
  • +{} равно NaN. Object становится "[object Object]", что не является допустимым числом, поэтому конвертируется в NaN.
  • "" + 1 равно "1". Потому что + встретил строку и выполнил конкатенацию вместо сложения.
  • [] + [] равно "". Каждый array преобразуется в пустую строку, а "" + "" даёт "".

Это не ошибки — они точно следуют правилам, описанным выше. Они лишь кажутся странными, потому что в одном выражении последовательно применяются два разных преобразования.

Совет

Лучшая практика: отдавайте предпочтение явному преобразованию. Запись Number(input), String(value) или Boolean(flag) делает ваше намерение очевидным для следующего читателя и обходит неожиданности неявного приведения типов. Используйте неявное преобразование только в очевидных случаях и применяйте строгое ===, чтобы значения никогда не изменялись незаметно при сравнениях.

Итоги

  • JavaScript преобразует значения в string, число или boolean — явно через String(), Number(), Boolean(), или неявно через приведение типов.
  • String: примитивы отображаются как текст; [1,2,3] становится "1,2,3", [] становится "", а обычный object становится "[object Object]".
  • Число: null равно 0, undefined равно NaN, "" равно 0; строки обрезаются, затем строго разбираются. Используйте +value как сокращение и parseInt/parseFloat (с основанием) для нестрогого разбора начала строки.
  • Boolean: только false, 0, -0, 0n, "", null, undefined и NaN являются falsy; всё остальное — включая "0", "false", [] и {} — является truthy.
  • + конкатенирует, если одна из сторон является строкой; каждый другой арифметический оператор конвертирует в число.
  • Если сомневаетесь — конвертируйте явно и сравнивайте через ===.

Вы будете постоянно использовать эти правила при работе с числами, строками и пользовательским вводом, хранящимся в переменных.

Проверьте свои знания

Практика
Что возвращает Number('')?
Что возвращает Number('')?
Практика
Какое из этих значений является truthy?
Какое из этих значений является truthy?
Практика
Каков результат выражения '5' + 3 в JavaScript?
Каков результат выражения '5' + 3 в JavaScript?
Was this page helpful?