Преобразование типов для примитивов

Система преобразования типов в JavaScript очень проста, но отличается от других языков. Поэтому она часто служит «камнем преткновения» для приходящих из других языков программистов.

Всего есть три преобразования:

  1. Строковое преобразование.
  2. Числовое преобразование.
  3. Преобразование к логическому значению.

Эта глава описывает преобразование только примитивных значений, объекты разбираются далее.

Строковое преобразование

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

var a = true;

alert( a ); // "true"

Можно также осуществить преобразование явным вызовом String(val):

alert( String(null) === "null" ); // true

Как видно из примеров выше, преобразование происходит наиболее очевидным способом, «как есть»: false становится "false", null"null", undefined"undefined" и т.п.

Также для явного преобразования применяется оператор "+", у которого один из аргументов строка. В этом случае он приводит к строке и другой аргумент, например:

alert( true + "test" ); // "truetest"
alert( "123" + undefined ); // "123undefined"

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

Численное преобразование происходит в математических функциях и выражениях, а также при сравнении данных различных типов (кроме сравнений ===, !==).

Для преобразования к числу в явном виде можно вызвать Number(val), либо, что короче, поставить перед выражением унарный плюс "+":

var a = +"123"; // 123
var a = Number("123"); // 123, тот же эффект
ЗначениеПреобразуется в...
undefinedNaN
null0
true / false1 / 0
СтрокаПробельные символы по краям обрезаются.
Далее, если остаётся пустая строка, то 0, иначе из непустой строки "считывается" число, при ошибке результат NaN.

Например:

// после обрезания пробельных символов останется "123"
alert( +"   \n  123   \n  \n" ); // 123

Ещё примеры:

  • Логические значения:

    alert( +true ); // 1
    alert( +false ); // 0
  • Сравнение разных типов – значит численное преобразование:

    alert( "\n0 " == 0 ); // true

    При этом строка "\n0" преобразуется к числу, как указано выше: начальные и конечные пробелы обрезаются, получается строка "0", которая равна 0.

  • С логическими значениями:

    alert( "\n" == false );
    alert( "1" == true );

    Здесь сравнение "==" снова приводит обе части к числу. В первой строке слева и справа получается 0, во второй 1.

Специальные значения

Посмотрим на поведение специальных значений более внимательно.

Интуитивно, значения null/undefined ассоциируются с нулём, но при преобразованиях ведут себя иначе.

Специальные значения преобразуются к числу так:

ЗначениеПреобразуется в...
undefinedNaN
null0

Это преобразование осуществляется при арифметических операциях и сравнениях > >= < <=, но не при проверке равенства ==. Алгоритм проверки равенства для этих значений в спецификации прописан отдельно (пункт 11.9.3). В нём считается, что null и undefined равны "==" между собой, но эти значения не равны никакому другому значению.

Это ведёт к забавным последствиям.

Например, null не подчиняется законам математики – он «больше либо равен нулю»: null>=0, но не больше и не равен:

alert( null >= 0 ); // true, т.к. null преобразуется к 0
alert( null > 0 ); // false (не больше), т.к. null преобразуется к 0
alert( null == 0 ); // false (и не равен!), т.к. == рассматривает null особо.

Значение undefined вообще «несравнимо»:

alert( undefined > 0 ); // false, т.к. undefined -> NaN
alert( undefined == 0 ); // false, т.к. это undefined (без преобразования)
alert( undefined < 0 ); // false, т.к. undefined -> NaN

Для более очевидной работы кода и во избежание ошибок лучше не давать специальным значениям участвовать в сравнениях > >= < <=.

Используйте в таких случаях переменные-числа или приводите к числу явно.

Логическое преобразование

Преобразование к true/false происходит в логическом контексте, таком как if(value), и при применении логических операторов.

Все значения, которые интуитивно «пусты», становятся false. Их несколько: 0, пустая строка, null, undefined и NaN.

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

Полная таблица преобразований:

ЗначениеПреобразуется в...
undefined, nullfalse
ЧислаВсе true, кроме 0, NaN -- false.
СтрокиВсе true, кроме пустой строки "" -- false
ОбъектыВсегда true

Для явного преобразования используется двойное логическое отрицание !!value или вызов Boolean(value).

Обратите внимание: строка "0" становится true

В отличие от многих языков программирования (например PHP), "0" в JavaScript является true, как и строка из пробелов:

alert( !!"0" ); // true
alert( !!" " ); // любые непустые строки, даже из пробелов - true!

Логическое преобразование интересно тем, как оно сочетается с численным.

Два значения могут быть равны, но одно из них в логическом контексте true, другое – false.

Например, равенство в следующем примере верно, так как происходит численное преобразование:

alert( 0 == "\n0\n" ); // true

… А в логическом контексте левая часть (0) даст false, правая ("\n0\n") – true, так как любая не пустая строка в логическом контексте равна true:

if ("\n0\n") {
  alert( "true, совсем не как 0!" );
}

С точки зрения преобразования типов в JavaScript это совершенно нормально. При сравнении с помощью «==» – численное преобразование, а в if – логическое, только и всего.

Итого

В JavaScript есть три преобразования:

  1. Строковое: String(value) – в строковом контексте или при сложении со строкой. Работает очевидным образом.
  2. Численное: Number(value) – в численном контексте, включая унарный плюс +value. Происходит при сравнении разных типов, кроме строгого равенства.
  3. Логическое: Boolean(value) – в логическом контексте, можно также сделать двойным НЕ: !!value.

Точные таблицы преобразований даны выше в этой главе.

Особым случаем является проверка равенства с null и undefined. Они равны друг другу, но не равны чему бы то ни было ещё, этот случай прописан особо в спецификации.

Задачи

важность: 5

Подумайте, какой результат будет у выражений ниже. Тут не только преобразования типов. Когда закончите – сверьтесь с решением.

"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
4 + 5 + "px"
"$" + 4 + 5

"4" - 2

"4px" - 2

7 / 0

"  -9\n" + 5
"  -9\n" - 5
5 && 2

2 && 5

5 || 0

0 || 5
null + 1
undefined + 1
null == "\n0\n"
+null == +"\n0\n"
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5
 = "$45"
"4" - 2
 = 2
"4px" - 2
 = NaN
7 / 0
 = Infinity
" -9\n" + 5 = " -9\n5"
" -9\n" - 5 = -14
5 && 2
 = 2
2 && 5
 = 5
5 || 0
 = 5
0 || 5 = 5
null + 1 = 1 // (3)
undefined + 1 = NaN // (4)
null == "\n0\n" = false // (5)
+null == +"\n0\n" = true // (6)
  1. Оператор "+" в данном случае прибавляет 1 как строку, и затем 0.
  2. Оператор "-" работает только с числами, так что он сразу приводит "" к 0.
  3. null при численном преобразовании становится 0
  4. undefined при численном преобразовании становится NaN
  5. При сравнении == с null преобразования не происходит, есть жёсткое правило: null == undefined и только.
  6. И левая и правая часть == преобразуются к числу 0.
Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.