HTML / CSSJavaScriptNode jsПаттерны проектированияПрактические

Приведение типов

Equality-Table

Преобразование типов может происходить явно и неявно.

Когда разработчик хочет намеренно произвести преобразование типов, написав, к примеру 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 ) // 10
3
4Number ( "string" )
5/*
6вернет специальное значение NaN ( Not a Number ),
7что означает, что строка "string" не может быть преобразована к числу
8*/
9
10String ( 50 ) // "50"
11
12Boolean ( "50" ) // true

Явное приведение к типу number

Во всех нижеперечисленных случаях результат будет 0:

1Number ( null ) // 0
2Number ( false ) // 0
3Number ( "" ) // 0
4Number ( " " ) // 0
5Number ( [] ) // 0
6Number ( "\n" ) // 0
7Number ( "\t" ) // 0
ℹ️note

"пробельные" символы "", " ", "\n", "\t" всегда приводятся к 0.

При преобразовании строки в число, движок сначала отсекает все пробельные символы, символы \n, и \t в начале и в конце строки, и возвращает NaN если обрезанная строка не представляет из себя корректное число. Если строка окажется пустой, то результатом будет 0.

1Number ( String.fromCharCode(9) ) // 0
2Number ( String.fromCharCode(10) ) // 0
3Number ( String.fromCharCode(11) ) // 0
4Number ( String.fromCharCode(12) ) // 0
5Number ( String.fromCharCode(13) ) // 0

String.fromCharCode( code ) возвращает символ, код которого равен code

1Number ( true ) //вернет 1

В случаях, когда преобразовать выражение к числу невозможно, результат будет NaN ( Not a Number ):

1Number ( 57 ) // вернет 57
2Number ( 4*"8" ) // вернет 32
3Number ( [5] ) // вернет 5
4Number ( [5]+[8] ) // вернет 58
5Number( null - true ) // вернет -1

Для приведения к целому числу или к числу с плавающей запятой ( с десятичными знаками ) можно использовать встроенные функции

  • parseInt
  • parseFloat

В отличие от конструктора Number, эти функции парсят строку, даже если в ней есть "левые" символы после числа - эти символы просто будут проигнорированы:

1Number ('3.14abc') // NaN
2parseFloat ('3.14abc') // 3.14
3parseInt ('3.14abc') // 3
4
5Number('3.14/5') // NaN
6parseFloat('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"
2
3var x = {}
4String ( x ) // "[object Object]"
5
6var 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 и т.д.

В общих чертах алгоритм выглядит следующим образом:

  1. Если входящее значение уже является примитивом, ничего не делать и просто вернуть его.
  2. Вызвать input.toString(), если результат примитив — вернуть его.
  3. Вызвать input.valueOf(), если результат примитив — вернуть его.
  4. Если ни один из методов не вернул примитив — бросить ошибку TypeError.

При численном преобразовании сначала вызывается метод valueOf(), а уже затем toString(). При строковом преобразовании наоборот — сначала происходит вызов toString(), а уже потом valueOf().

Большинство встроенных типов не имеют метода valueOf или же имеют valueOf, который возвращает свой собственный объект this, который игнорируется, так как this не является примитивом.

Вот почему численное и строковое преобразование в большинстве случаев работает одинаково — оба в конечном итоге вызывают метод toString().

1Object ( 5 + 8 + false )
1▼ Number {13}
2 ► __proto__: Number
3 [[PrimitiveValue]]: 13
1var x = 10
2Object ( x )
1▼ Number {10}
2 ► __proto__: Number
3 [[PrimitiveValue]]: 10
1var y = [ 5, true, "hello", 11 ]
2Object ( y )

Преобразования не будет, поскольку тип данных переменной y уже object.

1true + false // 1
212 / "6" // 2
3"number" + 15 + 3 // 'number153'
415 + 3 + "number" // '18number'
5[1] > null // true
6"foo" + + "bar" // 'fooNaN'
7'true' == true // false
8false == 'false' // false
9null == '' // false
10!!"false" == !!"true" // true
11['x'] == 'x' // true
12[] + null + 1 // 'null1'
13[1,2,3] == [1,2,3] // false
14{}+[]+{}+[1] // '0[object Object]1'
15!+[]+[]+![] // 'truefalse'
16new Date(0) - 0 // 0
17new Date(0) + 0 // 'Thu Jan 01 1970 02:00:00(EET)0'

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

Неявное приведение типов происходит в процессе вычисления выражений

Неявное приведение к типу string

При сложении числа и строки JavaScript обрабатывает число как строку

1// после выполнения кода:
2var res = 20 + "5"
3/* значением переменной res будет строка "205"
4JavaScript вычисляет выражения слева направо
5
6В результате выполнения кода:
7 */
8res = 20 + 10 + "5"
9// в переменной res будет значение "305", а в результате выполнения кода:
10res = "3" + 20 + 10
11// в переменной 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 ] // 4
2+[5] + null // 5

Неявное приведение к типу number

1var x = "8" / 2 // значением переменной x будет 4 )

При участии в арифметических операциях пустая строка "" и пустой массив [] преобразуется в 0:

1var x = ""
2var y = x / 5 // ( выражение "" / 5 будет приведено к 0 / 5 )
1console.log ( +"" ) // 0
2console.log ( +[] ) // 0
3console.log ( +[]+"" ) // 0

Если в арифметическом выражении участвуют специальные значения undefined или null, то они преобразуются к числу так:

1Number ( undefined ) // NaN
2Number ( null ) // 0

Если в арифметическом выражении участвуют логические значения true или false, то они преобразуются к числу так:

1var a = false
2var b = true
3var z = a + b // 0 + 1 --> 1
  1. Существует два специальных правила которые следует запомнить: При применении == к null или undefined, численное преобразование не происходит, так как null может равняться только null или undefined, и ничему другому.
1null == 0 // false, null is not converted to 0
2null == null // true
3undefined == undefined // true
4null == undefined // true

2 NaN не равен ничему, даже самому себе.

Кроме арифметических операций, преобразование к типу number происходит при участии переменной в операциях сравнения ( за исключением операций === и !==, когда сравниваются не только значения, но и типы данных )

1a = false, b = undefined
2a > b // 0 > NaN --> false
3a < b // 0 < NaN --> false
4a == b // 0 == NaN --> false
5
6a = true, b = null
7a > b // 1 > null --> true ( 1 > 0 )
8a < b // 1 < null --> false
9a == b // 1 == null --> false

Неявное приведение к типу boolean

Преобразование типов к логическому типу ( boolean ) происходит в условных операторах ( if, тернарный оператор )

1if ( "5" ) console.log ( "Yes" )

Будет вычисляться логическое значение выражения в круглых скобках оператора if, т.е. "под капотом" будет выполнена операция

1Boolean ( "5" )

При выполнении логических операций || и && происходит неявное приведение типов операндов к логическому значению, но при этом результатом логической операции будет изначальное значение одного из операндов, даже если оно не являются булевым

Операция && перебирает операнды слева направо, приводя их к логическому значению, до тех пор, пока не встретится первый false

в этом случае возвращается исходное значение последнего операнда

1true && false && null // false
2true && "5" && null // null
3true && [] && null // null
4
5true && ![] && null // false
6// вычисляется значение второго операнда ![], оно будет false, операция останавливается и возвращается последний операнд, на котором остановились )
7
8true && true && true && true // true
9//дошли до конца, но не встретили false, возвращается последний операнд

Операция || перебирает операнды слева направо, приводя их к логическому значению, до тех пор, пока не встретится первый true в этом случае возвращается исходное значение последнего операнда, на котором остановились

1null || false || 5 || "" // 5
2null || "" || 0 || 4 || 10 // 4
3
4null || false || undefined || "" // ""

последовательно вычисляются логические значения первого операнда ( null) - это false, второго операнда - false, третьего операнда ( undefined ) - это false, четвертого операнда ( "" ) - это false больше операндов нет, операция завершается и возвращает последний операнд, на котором остановилась ( "" )

!! можно привести переменную любого типа к boolean с помощью логической операции двойного отрицания: (!!)

1var x = null
2var y = !!x // false
3x = undefined
4y = !!x // false
5!![ ] // вернет true
6!!+[ ] // вернет false

Таблица равенства

Original source

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

Hello