Приведение типов
Преобразование типов может происходить явно и неявно.
Когда разработчик хочет намеренно произвести преобразование типов, написав, к примеру Number(value)
, это называется явным преобразованием типов (или type casting).
Так как JavaScript это слабо типизированный язык, преобразование между разными типами может происходить автоматически, и это называется неявным преобразованием типов.
Чаще всего это происходит когда вы применяете операторы к значениям разных типов, таких как 1 == null
, 2 / '5'
, null + new Date()
,
может происходить в зависимости от контекста, как например, в случае с if (value) {…}
, где value
будет приведено к булевому значению.
Важно
Оператор строгого равенства ===
не приводит к неявному преобразованию типов.
Оператор нестрогого равенства ==
, в свою очередь, производит сравнение операндов и, если требуется, неявное преобразование типов.
Неявное преобразование типов
Неявное преобразование типов — это палка о двух концах: с одной стороны это источник проблем и разочарований, а с другой — механизм, который позволяет нам писать меньше кода, не теряя при этом читабельности.
Во-первых, следует знать, что в JavaScript существует всего 3 типа преобразования:
- строковое
- булевое
- численное
Во-вторых, логика преобразования для примитивов и объектов работает по-разному, но, и примитивы и объекты могут быть преобразованы только этими тремя способами.
Явное приведение типов
note
Самый простой способ явного приведения данных произвольного типа к типу string, number или boolean - использование встроенных одноименных функций:
String()
Number()
Boolean()
1var x = "10"2Number ( x ) // 1034Number ( "string" )5/*6вернет специальное значение NaN ( Not a Number ),7что означает, что строка "string" не может быть преобразована к числу8*/910String ( 50 ) // "50"1112Boolean ( "50" ) // true
Явное приведение к типу number
Во всех нижеперечисленных случаях результат будет 0:
1Number ( null ) // 02Number ( false ) // 03Number ( "" ) // 04Number ( " " ) // 05Number ( [] ) // 06Number ( "\n" ) // 07Number ( "\t" ) // 0
note
"пробельные" символы "", " ", "\n", "\t" всегда приводятся к 0.
При преобразовании строки в число, движок сначала отсекает все пробельные символы, символы \n
, и \t
в начале и в конце строки,
и возвращает NaN
если обрезанная строка не представляет из себя корректное число.
Если строка окажется пустой, то результатом будет 0
.
1Number ( String.fromCharCode(9) ) // 02Number ( String.fromCharCode(10) ) // 03Number ( String.fromCharCode(11) ) // 04Number ( String.fromCharCode(12) ) // 05Number ( String.fromCharCode(13) ) // 0
String.fromCharCode( code )
возвращает символ, код которого равен code
1Number ( true ) //вернет 1
В случаях, когда преобразовать выражение к числу невозможно, результат будет NaN
( Not a Number ):
1Number ( 57 ) // вернет 572Number ( 4*"8" ) // вернет 323Number ( [5] ) // вернет 54Number ( [5]+[8] ) // вернет 585Number( null - true ) // вернет -1
Для приведения к целому числу или к числу с плавающей запятой ( с десятичными знаками ) можно использовать встроенные функции
В отличие от конструктора Number, эти функции парсят строку, даже если в ней есть "левые" символы после числа - эти символы просто будут проигнорированы:
1Number ('3.14abc') // NaN2parseFloat ('3.14abc') // 3.143parseInt ('3.14abc') // 345Number('3.14/5') // NaN6parseFloat('3.14/5') // 3.14
Однако если строка начинается с символов, которые не могут быть приведены к числу, эти функции вернут NaN
.
Явное приведение к типу boolean
Во всех нижеперечисленных случаях результат будет false
:
1Boolean ( "" )2Boolean ( 0 )3Boolean ( -0 )4Boolean ( NaN )5Boolean ( null )6Boolean ( undefined )7Boolean ( false )
Во всех остальных случаях результат будет true
При приведении строки к булевому типу действует простое правило:
если длина строки равна 0
, то возвращается false
, в противном случае - true
Явное приведение к типу string
1var str = String ( 5 + 8 + false ) // "13"23var x = {}4String ( x ) // "[object Object]"56var y = [ 5, true, "hello", 11 ]7String ( y ) // "5,true,hello,11"
При приведении числа к типу string можно использовать метод toString()
, который принимает один аргумент - десятичное число 2
, 8
или 16
( система исчисления )
Десятичная система исчисления подразумевается по умолчанию, поэтому аргумент при этом можно опустить
Для того, чтобы получить строчное значение числа в двоичной системе исчисления, нужно передать методу toString()
аргумент 2
, в восьмеричной - 8
, в шестнадцатеричной - 16
1Number(2).toString(2) // "10"2Number(58).toString(2) // "111010"3Number(8).toString(8) // "10"4Number(58).toString(8) // "72"5Number(16).toString(16) // "10"6Number(58).toString(16) // "3a"
Явное приведение к типу object
Объекты приводятся к примитивам посредством вызова внутреннего метода [[ToPrimitive]]
, который отвечает как за численное, так и за строковое преобразование.
Как для строкового так и для численного преобразования используются два метода объекта: valueOf
и toString
.
Оба метода объявлены в Object.prototype
, а значит доступны для всех производных типов, таких как Date
, Array
и т.д.
В общих чертах алгоритм выглядит следующим образом:
- Если входящее значение уже является примитивом, ничего не делать и просто вернуть его.
- Вызвать
input.toString()
, если результат примитив — вернуть его. - Вызвать
input.valueOf()
, если результат примитив — вернуть его. - Если ни один из методов не вернул примитив — бросить ошибку
TypeError
.
При численном преобразовании сначала вызывается метод valueOf()
, а уже затем toString()
.
При строковом преобразовании наоборот — сначала происходит вызов toString()
, а уже потом valueOf()
.
Большинство встроенных типов не имеют метода valueOf
или же имеют valueOf
, который возвращает свой собственный объект this
,
который игнорируется, так как this
не является примитивом.
Вот почему численное и строковое преобразование в большинстве случаев работает одинаково — оба в конечном итоге вызывают метод toString()
.
1Object ( 5 + 8 + false )
1▼ Number {13}2 ► __proto__: Number3 [[PrimitiveValue]]: 13
1var x = 102Object ( x )
1▼ Number {10}2 ► __proto__: Number3 [[PrimitiveValue]]: 10
1var y = [ 5, true, "hello", 11 ]2Object ( y )
Преобразования не будет, поскольку тип данных переменной y
уже object
.
1true + false // 1212 / "6" // 23"number" + 15 + 3 // 'number153'415 + 3 + "number" // '18number'5[1] > null // true6"foo" + + "bar" // 'fooNaN'7'true' == true // false8false == 'false' // false9null == '' // false10!!"false" == !!"true" // true11['x'] == 'x' // true12[] + null + 1 // 'null1'13[1,2,3] == [1,2,3] // false14{}+[]+{}+[1] // '0[object Object]1'15!+[]+[]+![] // 'truefalse'16new Date(0) - 0 // 017new Date(0) + 0 // 'Thu Jan 01 1970 02:00:00(EET)0'
Неявное приведение типов
Неявное приведение типов происходит в процессе вычисления выражений
Неявное приведение к типу string
При сложении числа и строки JavaScript обрабатывает число как строку
1// после выполнения кода:2var res = 20 + "5"3/* значением переменной res будет строка "205"4JavaScript вычисляет выражения слева направо56В результате выполнения кода:7 */8res = 20 + 10 + "5"9// в переменной res будет значение "305", а в результате выполнения кода:10res = "3" + 20 + 1011// в переменной res будет значение "32010"
При сложении массива и любого другого операнда результат будет строкового типа ( string
)
1[ ] + 5 // "5"2[ ] + false // "false"3[ 4 ] + NaN // "4NaN"4[ 4, 8 ] + null // "4,8null"5null + [ 4, 8 ] // "null4,8"
Это происходит потому, что массив преобразуется в строку:
1String ( [ 4, 8 ] ) // результат будет 4,8
1null + +[ 4 ] // 42+[5] + null // 5
Неявное приведение к типу number
1var x = "8" / 2 // значением переменной x будет 4 )
При участии в арифметических операциях пустая строка ""
и пустой массив []
преобразуется в 0
:
1var x = ""2var y = x / 5 // ( выражение "" / 5 будет приведено к 0 / 5 )
1console.log ( +"" ) // 02console.log ( +[] ) // 03console.log ( +[]+"" ) // 0
Если в арифметическом выражении участвуют специальные значения undefined
или null
, то они преобразуются к числу так:
1Number ( undefined ) // NaN2Number ( null ) // 0
Если в арифметическом выражении участвуют логические значения true
или false
, то они преобразуются к числу так:
1var a = false2var b = true3var z = a + b // 0 + 1 --> 1
- Существует два специальных правила которые следует запомнить:
При применении
==
кnull
илиundefined
, численное преобразование не происходит, так какnull
может равняться толькоnull
илиundefined
, и ничему другому.
1null == 0 // false, null is not converted to 02null == null // true3undefined == undefined // true4null == undefined // true
2 NaN
не равен ничему, даже самому себе.
Кроме арифметических операций, преобразование к типу number
происходит при участии переменной в операциях сравнения
( за исключением операций ===
и !==
, когда сравниваются не только значения, но и типы данных )
1a = false, b = undefined2a > b // 0 > NaN --> false3a < b // 0 < NaN --> false4a == b // 0 == NaN --> false56a = true, b = null7a > b // 1 > null --> true ( 1 > 0 )8a < b // 1 < null --> false9a == b // 1 == null --> false
Неявное приведение к типу boolean
Преобразование типов к логическому типу ( boolean ) происходит в условных операторах ( if, тернарный оператор )
1if ( "5" ) console.log ( "Yes" )
Будет вычисляться логическое значение выражения в круглых скобках оператора if, т.е. "под капотом" будет выполнена операция
1Boolean ( "5" )
При выполнении логических операций ||
и &&
происходит неявное приведение типов операндов к логическому значению, но при этом результатом
логической операции будет изначальное значение одного из операндов, даже если оно не являются булевым
Операция &&
перебирает операнды слева направо, приводя их к логическому значению, до тех пор, пока не встретится первый false
в этом случае возвращается исходное значение последнего операнда
1true && false && null // false2true && "5" && null // null3true && [] && null // null45true && ![] && null // false6// вычисляется значение второго операнда ![], оно будет false, операция останавливается и возвращается последний операнд, на котором остановились )78true && true && true && true // true9//дошли до конца, но не встретили false, возвращается последний операнд
Операция ||
перебирает операнды слева направо, приводя их к логическому значению, до тех пор, пока не встретится первый true
в этом случае возвращается исходное значение последнего операнда, на котором остановились
1null || false || 5 || "" // 52null || "" || 0 || 4 || 10 // 434null || false || undefined || "" // ""
последовательно вычисляются логические значения
первого операнда ( null
) - это false
,
второго операнда - false
,
третьего операнда ( undefined
) - это false
,
четвертого операнда ( "" ) - это false
больше операндов нет, операция завершается и возвращает последний операнд, на котором остановилась ( "" )
!!
можно привести переменную любого типа к boolean с помощью логической операции двойного отрицания: (!!
)
1var x = null2var y = !!x // false3x = undefined4y = !!x // false5!![ ] // вернет true6!!+[ ] // вернет false