Приведение типов
Преобразование типов может происходить явно и неявно.
Когда разработчик хочет намеренно произвести преобразование типов, написав, к примеру Number(value) , это называется явным преобразованием типов (или type casting).
Так как JavaScript это слабо типизированный язык, преобразование между разными типами может происходить автоматически, и это называется неявным преобразованием типов.
Чаще всего это происходит когда вы применяете операторы к значениям разных типов, таких как 1 == null , 2 / ‘5’ , null + new Date() , может происходить в зависимости от контекста, как например, в случае с if (value) , где value будет приведено к булевому значению.
ℹ️ Важно
Оператор строгого равенства === не приводит к неявному преобразованию типов. Оператор нестрогого равенства == , в свою очередь, производит сравнение операндов и, если требуется, неявное преобразование типов.
🔥 Неявное преобразование типов
Неявное преобразование типов — это палка о двух концах: с одной стороны это источник проблем и разочарований, а с другой — механизм, который позволяет нам писать меньше кода, не теряя при этом читабельности.
Во-первых, следует знать, что в JavaScript существует всего 3 типа преобразования:
Во-вторых, логика преобразования для примитивов и объектов работает по-разному, но, и примитивы и объекты могут быть преобразованы только этими тремя способами.
Явное приведение типов
ℹ️ note
Самый простой способ явного приведения данных произвольного типа к типу string, number или boolean — использование встроенных одноименных функций:
1var x = "10"2 Number ( x ) // 1034 Number ( "string" )5 /*6вернет специальное значение NaN ( Not a Number ),7что означает, что строка "string" не может быть преобразована к числу8*/910 String ( 50 ) // "50"1112 Boolean ( "50" ) // true
Явное приведение к типу number
Во всех нижеперечисленных случаях результат будет 0:
1Number ( null ) // 02 Number ( false ) // 03 Number ( "" ) // 04 Number ( " " ) // 05 Number ( [] ) // 06 Number ( "\n" ) // 07 Number ( "\t" ) // 0
ℹ️ note
«пробельные» символы «», » «, «\n», «\t» всегда приводятся к 0.
При преобразовании строки в число, движок сначала отсекает все пробельные символы, символы \n , и \t в начале и в конце строки, и возвращает NaN если обрезанная строка не представляет из себя корректное число. Если строка окажется пустой, то результатом будет 0 .
1Number ( String.fromCharCode(9) ) // 02 Number ( String.fromCharCode(10) ) // 03 Number ( String.fromCharCode(11) ) // 04 Number ( String.fromCharCode(12) ) // 05 Number ( String.fromCharCode(13) ) // 0
String.fromCharCode( code ) возвращает символ, код которого равен code
В случаях, когда преобразовать выражение к числу невозможно, результат будет NaN ( Not a Number ):
1Number ( 57 ) // вернет 572 Number ( 4*"8" ) // вернет 323 Number ( [5] ) // вернет 54 Number ( [5]+[8] ) // вернет 585 Number( null - true ) // вернет -1
Для приведения к целому числу или к числу с плавающей запятой ( с десятичными знаками ) можно использовать встроенные функции
В отличие от конструктора Number, эти функции парсят строку, даже если в ней есть «левые» символы после числа — эти символы просто будут проигнорированы:
1Number ('3.14abc') // NaN2 parseFloat ('3.14abc') // 3.143 parseInt ('3.14abc') // 345 Number('3.14/5') // NaN6 parseFloat('3.14/5') // 3.14
Однако если строка начинается с символов, которые не могут быть приведены к числу, эти функции вернут NaN .
Явное приведение к типу boolean
Во всех нижеперечисленных случаях результат будет false :
1Boolean ( "" )2 Boolean ( 0 )3 Boolean ( -0 )4 Boolean ( NaN )5 Boolean ( null )6 Boolean ( undefined )7 Boolean ( false )
Во всех остальных случаях результат будет true
При приведении строки к булевому типу действует простое правило:
если длина строки равна 0 , то возвращается false , в противном случае — true
Явное приведение к типу string
1var str = String ( 5 + 8 + false ) // "13"23 var x = >4 String ( x ) // "[object Object]"56 var y = [ 5, true, "hello", 11 ]7 String ( y ) // "5,true,hello,11"
При приведении числа к типу string можно использовать метод toString() , который принимает один аргумент — десятичное число 2 , 8 или 16 ( система исчисления )
Десятичная система исчисления подразумевается по умолчанию, поэтому аргумент при этом можно опустить
Для того, чтобы получить строчное значение числа в двоичной системе исчисления, нужно передать методу toString() аргумент 2 , в восьмеричной — 8 , в шестнадцатеричной — 16
1Number(2).toString(2) // "10"2 Number(58).toString(2) // "111010"3 Number(8).toString(8) // "10"4 Number(58).toString(8) // "72"5 Number(16).toString(16) // "10"6 Number(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() .
1▼ Number2 ► __proto__: Number3 [[PrimitiveValue]]: 13
1▼ Number2 ► __proto__: Number3 [[PrimitiveValue]]: 10
1var y = [ 5, true, "hello", 11 ]2 Object ( y )
Преобразования не будет, поскольку тип данных переменной y уже object .
1true + false // 12 12 / "6" // 23 "number" + 15 + 3 // 'number153'4 15 + 3 + "number" // '18number'5 [1] > null // true6 "foo" + + "bar" // 'fooNaN'7 'true' == true // false8 false == 'false' // false9 null == '' // 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'16 new Date(0) - 0 // 017 new Date(0) + 0 // 'Thu Jan 01 1970 02:00:00(EET)0'
Неявное приведение типов
Неявное приведение типов происходит в процессе вычисления выражений
Неявное приведение к типу string
При сложении числа и строки JavaScript обрабатывает число как строку
1// после выполнения кода:2 var 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
Неявное приведение к типу number
1var x = "8" / 2 // значением переменной x будет 4 )
При участии в арифметических операциях пустая строка «» и пустой массив [] преобразуется в 0 :
1var x = ""2 var y = x / 5 // ( выражение "" / 5 будет приведено к 0 / 5 )
1console.log ( +"" ) // 02 console.log ( +[] ) // 03 console.log ( +[]+"" ) // 0
Если в арифметическом выражении участвуют специальные значения undefined или null , то они преобразуются к числу так:
1Number ( undefined ) // NaN2 Number ( null ) // 0
Если в арифметическом выражении участвуют логические значения true или false , то они преобразуются к числу так:
1var a = false2 var b = true3 var z = a + b // 0 + 1 --> 1
- Существует два специальных правила которые следует запомнить: При применении == к null или undefined , численное преобразование не происходит, так как null может равняться только null или undefined , и ничему другому.
1null == 0 // false, null is not converted to 02 null == null // true3 undefined == undefined // true4 null == 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, тернарный оператор )
Будет вычисляться логическое значение выражения в круглых скобках оператора if, т.е. «под капотом» будет выполнена операция
При выполнении логических операций || и && происходит неявное приведение типов операндов к логическому значению, но при этом результатом логической операции будет изначальное значение одного из операндов, даже если оно не являются булевым
Операция && перебирает операнды слева направо, приводя их к логическому значению, до тех пор, пока не встретится первый false
в этом случае возвращается исходное значение последнего операнда
1true && false && null // false2 true && "5" && null // null3 true && [] && null // null45 true && ![] && null // false6 // вычисляется значение второго операнда ![], оно будет false, операция останавливается и возвращается последний операнд, на котором остановились )78 true && true && true && true // true9 //дошли до конца, но не встретили false, возвращается последний операнд
Операция || перебирает операнды слева направо, приводя их к логическому значению, до тех пор, пока не встретится первый true в этом случае возвращается исходное значение последнего операнда, на котором остановились
1null || false || 5 || "" // 52 null || "" || 0 || 4 || 10 // 434 null || false || undefined || "" // ""
последовательно вычисляются логические значения первого операнда ( null ) — это false , второго операнда — false , третьего операнда ( undefined ) — это false , четвертого операнда ( «» ) — это false больше операндов нет, операция завершается и возвращает последний операнд, на котором остановилась ( «» )
!! можно привести переменную любого типа к boolean с помощью логической операции двойного отрицания: ( !! )
1var x = null2 var y = !!x // false3x = undefined4y = !!x // false5 !![ ] // вернет true6 !!+[ ] // вернет false