Перетворення типів
Найчастіше оператори та функції автоматично наводять передані їм значення до потрібного типу.
Наприклад, alert автоматично перетворює будь-яке значення до рядка. Математичні оператори перетворять значення до чисел.
Є також випадки, коли нам потрібно явно перетворити значення на очікуваний тип.
У цьому розділі ми не торкаємось об'єктів. Спочатку ми розберемо перетворення примітивних значень.
Ми розберемо перетворення об'єктів пізніше, у розділі Перетворення об'єктів на примітиви.
Строкове перетворення
Рядкове перетворення відбувається, коли потрібно подання чогось у вигляді рядка.
Наприклад, alert(value) перетворює значення до рядка.
Також ми можемо використовувати функцію String(value) , щоб перетворити значення до рядка:
let value = true; alert(typeof value); // boolean value = String(value); // тепер value це рядок "true" alert(typeof value); // string
Перетворення відбувається очевидним чином. false стає "false", null стає "null" і т.п.
Чисельне перетворення
Чисельне перетворення відбувається в математичних функціях та виразах.
Наприклад, коли операція поділу / застосовується не до:
alert("6"/"2"); // 3, рядки перетворюються на числа
Ми можемо використовувати функцію Number(value) , щоб явно перетворити value до:
let str = "123"; alert(typeof str); // string let num = Number (str); // стає числом 123 alert(type of num); // number
Явне перетворення часто застосовується, коли ми очікуємо отримати число з рядкового контексту, наприклад текстових полів форм.
Якщо рядок не може бути явно наведений до числа, то результатом перетворення буде NaN . Наприклад:
let age = Number("Будь-який рядок замість числа"); alert(age); // NaN, перетворення не вдалося
Правила чисельного перетворення:
| Значення |
Перетворюється на… |
| undefined |
NaN |
| null |
0 |
| true / false |
1 / 0 |
| string |
Пробільні символи (пробіли, знаки табуляції \t, знаки нового рядка \n і т. п.) по краях обрізаються. |
alert( Number(" 123 ") ; // 123 alert( Number("123z") ); // NaN (помилка читання числа на місці символу "z") alert( Number(true) ); Number(false));
Врахуйте, що null і undefined поводяться по-різному. Так, null стає нулем, тоді як undefined приводиться до NaN.
Більшість математичних операторів також здійснює це перетворення, як ми побачимо в наступному розділі.
Логічне перетворення
Логічне перетворення найпростіше.
Відбувається в логічних операціях (пізніше ми познайомимося з умовними перевірками та подібними конструкціями), але також може бути виконано за допомогою функції Boolean (value).
- Значення, які інтуїтивно «порожні», на зразок 0, порожнього рядка, null, undefined і NaN, стають false.
- Всі інші значення стають true.
alert( Boolean(1) ); // true alert( Boolean(0) ); // false alert( "Привіт!") ;
Деякі мови (наприклад, PHP) сприймають рядок "0" як false. Але в JavaScript, якщо рядок не порожній, то він завжди true.
alert( Boolean("0") ); // true alert( Boolean(" ") );
Разом
Існує 3 найбільш широко використовувані перетворення: рядкове, чисельне та логічне.
Строкове - Відбувається, коли нам потрібно щось вивести.
Чисельне – Відбувається в математичних операціях.
Перетворення підпорядковується правилам:
| Значення |
Стає… |
| undefined |
NaN |
| null |
0 |
| true / false |
1 / 0 |
| string |
Пробіли по краях обрізаються. Далі, якщо залишається порожній рядок, то отримуємо 0, інакше з непустого рядка «зчитується» число. При помилці результат NaN. |
Логічне – Відбувається у логічних операціях. Може бути викликано за допомогою Boolean (value).
| Значення |
Стає… |
| 0, null, undefined, NaN, "" |
false |
| будь-яке інше значення |
true |
Більшість цих правил легко зрозуміти і запам'ятати. Особливі випадки, в яких часто допускаються помилки:
- undefined при чисельному перетворенні стає NaN , не 0 .
- "0" і рядки з одних пробілів типу "" при логічному перетворенні завжди true.
У цьому розділі ми не говорили про об'єкти. Ми повернемося до них пізніше, у розділі Перетворення об'єктів на примітиви, присвяченій тільки об'єктам, відразу після того, як дізнаємося більше про основи JavaScript.
Перетворення об'єктів: toString і valueOf
Матеріал на цій сторінці застарів, тому прихований із змісту сайту.
Новіша інформація з цієї теми знаходиться на сторінці https://learn.javascript.ru/object-toprimitive.
Раніше у розділі Перетворення типів для примітивів ми розглядали перетворення типів для примітивів. Тепер додамо до нашої картини світу об'єкти.
Бувають операції, при яких об'єкт повинен бути перетворений на примітив.
- Рядкове перетворення – якщо об'єкт виводиться через alert(obj) .
- Чисельне перетворення – при арифметичних операціях порівняно з примітивом.
- Логічне перетворення – при if(obj) та інших логічних операціях.
Розглянемо ці перетворення по черзі.
Логічне перетворення
Найпростіше – з логічним перетворенням.
Будь-який об'єкт у логічному контексті - true, навіть якщо це порожній масив [] або об'єкт <>.
Строкове перетворення
Рядкове перетворення найпростіше побачити, якщо вивести об'єкт за допомогою alert :
var user = < firstName: 'Василь' >; alert(user); // [Object Object]
Як бачимо, вміст об'єкта не вивелося. Це тому, що стандартним рядковим уявленням об'єкта користувача є рядок "[object Object]" .
Такий висновок об'єкта не містить цікавої інформації. Тому має сенс його поміняти на щось корисніше.
Якщо в об'єкті є метод toString , який повертає примітив, то він використовується для перетворення.
var user = < firstName: 'Василь', toString: function() < return 'Користувач ' + this.firstName; >>; alert(user); // Користувач Василь
Метод toString не повинен повертати саме рядок.
Його результат може бути будь-якого примітивного типу. Наприклад, це може бути число, як у прикладі нижче:
var obj = < toString: function() < return 123; >>; alert(obj); // 123
Тому ми і називаємо його тут «рядкове перетворення», а не "перетворення до рядка".
Всі об'єкти, включаючи вбудовані, мають свої реалізації методу toString , наприклад:
alert([1, 2]); // toString для масивів виводить список елементів "1,2" alert(new Date); // toString для дат виводить дату як рядка alert( function() <> ); // toString для функції виводить її код
Чисельне перетворення
Для чисельного перетворення об'єкта використовується метод valueOf , а якщо його немає – то toString :
var room = < number: 777, valueOf: function() < return this.number; >, toString: function() < return this.number; >>; alert(+room); // 777, зголосився valueOf delete room.valueOf; // valueOf видалено alert( +room ); // 777, зголосився toString
Метод valueOf повинен повертати примітивне значення, інакше його результат буде проігноровано. При цьому не обов'язково числове.
У більшості вбудованих об'єктів такого значення немає, тому чисельне і рядкове перетворення для них працюють однаково.
Винятком є об'єкт Date , який підтримує обидва типи перетворень:
alert(new Date()); // toString: Дата у вигляді рядка, що читається alert( +new Date() ); // valueOf: у мілісекунд, що пройшли з 01.01.1970
Якщо подивитися в стандарті, то в пункті 15.2.4.4 йдеться про те, що valueOf є у будь-яких об'єктів. Але він нічого не робить, просто повертає сам об'єкт (непримітивне значення!), а тому ігнорується.
Дві стадії перетворення
Отже, об'єкт перетворений на примітив за допомогою toString або valueOf .
Але на цьому перетворення не обов'язково закінчуються. Цілком можливо, що в процесі обчислень цей примітив буде перетворено на щось інше.
Наприклад, розглянемо застосування до об'єкта операції == :
var obj = < valueOf: function() < return 1; >>; alert(obj == true); // true
Об'єкт obj був спочатку перетворений на примітив, використовуючи чисельне перетворення, вийшло 1 == true .
Далі, оскільки значення все ще різних типів застосовуються правила перетворення примітивів, результат: true .
Те саме – при складанні з об'єктом за допомогою + :
var obj = < valueOf: function() < return 1; >>; alert(obj + "test"); // 1test
Або ось, для різниці об'єктів:
var a = < valueOf: function() < return "1"; >>; var b = < valueOf: function() < return "2"; >>; alert(a + b); // "12" alert(a - b); // "1" - "2" = -1
Об'єкт Date з історичних причин є винятком.
Бінарний оператор плюс + зазвичай використовує чисельне перетворення та метод valueOf. Як ми вже знаємо, якщо відповідного valueOf немає (а його немає у більшості об'єктів), то використовується toString , так що в результаті перетворення відбувається до рядка. Але якщо є valueOf, то використовується valueOf. Вище в прикладі якраз a + b це демонструють.
Об'єкти Date мають і valueOf – повертає кількість мілісекунд, і toString – повертає рядок з датою.
… Але оператор + для Date використовує саме toString (хоча повинен би valueOf ).
Це і є виняток:
// бінарний плюс для дати toString, для інших об'єктів valueOf alert( new Date + ""); // "Рядок дати"
Інших подібних винятків немає.
У мові Java (це JavaScript, інша мова, тут наведено для прикладу) логічні значення можна створювати, використовуючи синтаксис new Boolean(true/false) , наприклад new Boolean(true) .
У JavaScript теж є подібна можливість, яка повертає об'єктну обгортку для логічного значення.
Ця можливість давно існує лише для сумісності, вона й не використовується на практиці, оскільки веде до дивних результатів. Деякі з них можуть сильно здивувати людину, яка не звикла до JavaScript, наприклад:
var value = New Boolean (false); if (value) <alert(true); // Спрацює! >
Чому запустився alert? Адже в if знаходиться false... Перевіримо:
var value = New Boolean (false); alert(value); // виводить false, все ок.. if (value) < alert( true ); // ..але тоді чому виконується alert в if. >
Справа в тому, що new Boolean - це не примітивне значення, а об'єкт. Тому в логічному контексті він перетворюється на true , в результаті працює перший приклад.
А другий приклад викликає alert, який перетворює об'єкт до рядка, і він стає "false".
У JavaScript виклики new Boolean/String/Number не використовуються, а використовуються прості виклики відповідних функцій, вони перетворять значення на примітив потрібного типу, наприклад Boolean(val) === !!val .
Разом
- У логічному контексті об'єкт завжди true .
- При рядковому перетворенні об'єкта використовується його метод toString. Він повинен повертати примітивне значення, причому не обов'язково саме рядок.
- Для чисельного перетворення використовується метод valueOf, який також може повернути будь-яке примітивне значення. У більшості об'єктів valueOf не працює (повертає сам об'єкт і тому ігнорується), при цьому для чисельного перетворення використовується toString.
Повний алгоритм перетворень є у специфікації ECMAScript, див. пункти 11.8.5, 11.9.3, а також 9.1 та 9.3.
Зауважимо, для повноти картини, що деякі тести знань в інтернеті пропонують питання типу:
<>[0] // чому одно? <> + <> // а так?
Якщо ви запустите ці вирази в консолі, результат може здатися дивним. Підступ тут у тому, що якщо фігурні дужки <. >ідуть не у виразі, а в основному потоці коду, то JavaScript вважає, що це не об'єкт, а блок коду (як if , for , але без оператора просто угруповання команд разом використовується рідко).
Ось блок коду з командою:
А якщо команду вилучити, то буде порожній блок, який нічого не робить. Два приклади вище містять порожній блок на початку, який нічого не робить. Інакше кажучи:
<>[0] // то же что и: [0] <> + <> // то же что и: + <>
Тобто, такі питання – не так на перетворення типів, але в розуміння, що й < . >перебуває виразів, це не об'єкт, а блок.