JS для начинающих. Урок 1.16: Приведение типов

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

function sum(a, b) {
    return a + b;
}

Результат выполнения этой функции не всегда будет таким, как нам, скорее всего, хотелось.

console.log(sum(1, 2)); // 3 (тут всё ок)
console.log(sum(1, '2')); // 12 (а тут не очень)

Как видно из примера функция sum некорректно себя ведёт, если в качестве хотя бы одного её аргумента передать не число. Дело в том, что при «сложении» числа со строкой, число преобразуется к строке и происходит его конкатенация (склеивание) со вторым операндом.

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

Оператор typeof

Этот унарный оператор принимает в качестве операнда абсолютно любое значение и возвращает его тип в строковой переменной.

В JavaScript существуют следующие типы данных:

// 1.) object
console.log(typeof {}); // object
var p = {x: 1, y: 3};
console.log(typeof p); // object
 
// 2.) function
function sayHello() {
    console.log("Hello!");
}
console.log(typeof sayHello); // function
 
// 3.) string
console.log(typeof "JavaScript"); // string
 
// 4.) number
console.log(typeof 3.1415); // number
 
// 5.) boolean
console.log(typeof true); // boolean
 
// 6.) undefined
var notExistsOne;
console.log(typeof notExistsOne); // undefined
console.log(typeof notExistsTwo); // undefined

Обратите внимание, что undefined это тоже тип данных, который состоит из одного значения.

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

Под приведением типов в программировании понимают преобразование значения переменной одного типа в значение другого типа.
Часто такое преобразование происходит без контроля со стороны программиста. Это можно было видеть в примере с функцией sum. Изменение типа происходит, если результат выполнения операции с переменной исходного типа неясен. Например, нельзя точно сказать, что получиться в результате сложения строки с числом, но операция сложения двух чисел очевидна, и в этом случае логично привести число к строке.

Преобразование строки к числу

Иногда сам программист может изменить тип переменной, применив к ней некоторые операции. Например, операции инкремента или декремента над строкой приведут её к числу.

var a = '1';
++a;
console.log(typeof a); // number
 
var b = '1';
--b;
console.log(typeof b); // number

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

var c = 'not-a-number';
++c;
console.log(typeof c); // NaN

Стоит заметить, что не нужно прибегать к такому способу приведения строки к числу из-за его плохой читабельности и неочевидности. Для этой задачи в js существуют встроенные функции parseInt и parseFloat. В качестве первого аргумента они принимают строку, которую необходимо привести к числу, а в качестве второго необязательного – основание системы счисления, в которой записано число в строке, передаваемой в качестве первого аргумента. Если второй аргумент не указан, то будет считаться, что в строке записано число в десятичной системе счисления.

Функция parseInt используется для преобразования строки в целое число, а функция parseFloat для преобразования в дробное.

var a = parseInt('10');
console.log(['a = ', a, 
    '; typeof a :', typeof a].join(' ')); // a =  10 ; typeof a : number
 
var pi = parseInt('3.1415');
console.log('pi = ' + pi); // pi = 3
 
pi = parseFloat('3.1415');
console.log('pi = ' + pi); // pi = 3.1415

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

a = parseInt('010');
console.log('a = ' + a); // a = 8
 
a = parseInt('0xAA');
console.log('a = ' + a); // a = 170
 
a = parseFloat('1e-10');
console.log('a = ' + a); // a = 1e-10 (1e-10 = 1 * 10 ^ -10 = 0.0000000001)

В качестве второго параметра функций parseInt и parseFloat можно указать основание системы счисления.

a = parseInt('10', 8);
console.log('a = ' + a); // a = 8
 
a = parseInt('010', 10);
console.log('a = ' + a); // a = 10
 
a = parseInt('ff', 16);
console.log('a = ' + a); // a = 255

В случае если значение стоящие в строке, которую функции parseInt и parseFloat принимают в качестве первого параметра, не представляет собой числовой литерал, то результатом выполнения этих функций будет значение NaN.

a = parseInt('not a number');
console.log('a = ' + a); // a = NaN
 
a = parseFloat('not a number');
console.log('a = ' + a); // a = NaN

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

В JavaScript значение любого типа можно привести к строке. Выше уже говорилось о том, что при конкатенации строки с числом, число приводится к строке, и лишь затем происходит конкатенация. Так будет происходить со значением любого типа.

var str = "Object: " + {};
console.log(str); // Object: [object Object]
 
str = "Array: " + [1, 2, 3];
console.log(str); // Array: 1,2,3
 
function sum(a, b) {
    return a + b;
}
 
str = 'Function: ' + sum;
console.log(str); /* Function: function sum(a, b) {
    return a + b;
} */

Фактически при приведении объекта к строке неявным образом вызывается метод toString, который так же можно вызвать явно.

var p = {x: 2, y: 4}, str;
str = p.toString();
console.log(typeof str); // string
console.log(str); // [object Object]
 
str = [1, 2, 3].toString();
console.log(typeof str); // string
console.log(str); // 1,2,3

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

Преобразование в число происходит при выполнении математических операций и при выполнении операции сравнения с приведением типа (==, !=), при этом значение false и пустой массив преобразуются в значение 0 типа number.

console.log (false == 0); // true
console.log([] == 0); // true

Логическое значение true при использовании в арифметических выражениях приводится к единице.

var a = true + true + true; // 1 + 1 + 1
console.log(a); // 3

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

var arr = [1, 2, 3];
console.log(arr + 4); // 1,2,34
 
function sum(a, b){return a + b;}
 
console.log(sum + 5); // function sum(a, b){return a + b;}5

Как видно, неявное преобразование типов в js далеко не всегда очевидно, поэтому стоит его избегать, используя функции для явного приведения типов, такие как parseInt, parseFloat и toString.

На этом всё. Как всегда, успехов вам!