Неявне приведення типів у Javascript (Implicit Coercion)
Andrew
Posted on July 30, 2023
Неявний примус у Javascript просто означає, що Javascript намагається примусити неочікуваний тип значення до очікуваного типу. Таким чином, ви можете передати рядок там, де він очікує число, об'єкт там, де він очікує рядок і т.д., і він спробує перетворити його до правильного типу.
3 * "3" // 9
1 + "2" + 1 // 121
true + true // 2
10 - true // 9
const foo = {
valueOf: () => 2
}
3 + foo // 5
4 * foo // 8
const bar = {
toString: () => " promise is a boy :)"
}
1 + bar // "1 promise is a boy :)"
4 * [] // 0
4 * [2] // 8
4 + [2] // "42"
4 + [1, 2] // "41,2"
4 * [1, 2] // NaN
"string" ? 4 : 1 // 4
undefined ? 4 : 1 // 1
Non-numeric values in numeric expressions
Рядки
Щоразу, коли ви передаєте рядок як операнд у числовому виразі, що містить будь-який з цих операторів: -, *, /, %
, процес перетворення числа подібний до виклику вбудованої функції Number
над значенням. Це досить просто: будь-який рядок, що містить лише цифрові символи, буде перетворено на його числовий еквівалент, але рядок, що містить нецифрові символи, поверне NaN
.
3 * "3" // 3 * 3
3 * Number("3") // 3 * 3
Number("5") // 5
Number("1.") // 1
Number("1.34") // 1.34
Number("0") // 0
Number("012") // 12
Number("1,") // NaN
Number("1+1") // NaN
Number("1a") // NaN
Number("one") // NaN
Number("text") // NaN
Випадок для оператора +
Оператор +, на відміну від інших математичних операторів, виконує дві функції:
- Математичне додавання;
- Конкатенація рядків.
Коли операндом оператора + є рядок, Javascript замість того, щоб перетворювати рядок на число, перетворює число на рядок.
// Concatenation
1 + "2" // "12"
1 + "js" // "1js"
// Addition
1 + 2 // 3
1 + 2 + 1 // 4
// Addition, then concatenation
1 + 2 + "1" // "31"
(1 + 2) + "1" // "31"
// Concatenation all through
1 + "2" + 1 // "121"
(1 + "2") + 1 // "121"
Об'єкти
Більшість перетворень об'єктів Javascript зазвичай призводять до [object Object]
.
"name" + {} // "name[object Object]"
Кожен об'єкт JavaScript успадковує метод toString
, який викликається щоразу, коли об'єкт потрібно перетворити в рядок. Значення, що повертається методом toString
, використовується для таких операцій, як конкатенація рядків і математичні вирази.
const foo = {}
foo.toString() // [object Object]
const baz = {
toString: () => "I'm object baz"
}
baz + "!" // "I'm object baz!"
Якщо це математичний вираз, Javascript спробує перетворити значення, що повертається, в число, якщо воно не є числом.
const foo = {
toString: () => 4
}
2 * foo // 8
2 / foo // 0.5
2 + foo // 6
"four" + foo // "four4"
const baz = {
toString: () => "four"
}
2 * baz // NaN
2 + baz // 2four
const bar = {
toString: () => "2"
}
2 + bar // "22"
2 * bar // 4
Об'єкти масивів
Успадкований метод toString
для масивів працює дещо інакше, він схожий на виклик методу join
масиву без аргументів.
[1,2,3].toString() // "1,2,3"
[1,2,3].join() // "1,2,3"
[].toString() // ""
[].join() // ""
"me" + [1,2,3] // "me1,2,3"
4 + [1,2,3] // "41,2,3"
4 * [1,2,3] // NaN
Отже, коли ви передаєте масив, де очікується рядок, Javascript об'єднує значення, що повертається методом toString
, з другим операндом. Якщо очікується число, він намагається перетворити значення, що повертається, в число.
4 * [] // 0
4 / [2] // 2
// Similar to
4 * Number([].toString())
4 * Number("")
4 * 0
//
4 / Number([2].toString())
4 / Number("2")
4 / 2
True, False та ""
Number(true) // 1
Number(false) // 0
Number("") // 0
4 + true // 5
3 * false // 0
3 * "" // 0
3 + "" // "3"
valueOf
метод
Також можна визначити метод valueOf
, який буде використовуватися Javascript щоразу, коли ви передаєте об'єкт, в якому очікується рядкове або числове значення.
const foo = {
valueOf: () => 3
}
3 + foo // 6
3 * foo // 9
Якщо на об'єкті визначено обидва методи toString
і valueOf
, Javascript використовує метод valueOf
.
const bar = {
toString: () => 2,
valueOf: () => 5
}
"sa" + bar // "sa5"
3 * bar // 15
2 + bar // 7
Метод valueOf
призначений для об'єктів, які мають представляти числове значення.
const two = new Number(2)
two.valueOf() // 2
Брехня і правда
Кожному значенню Javascript можна надати значення true
або false
. Примус до булевого значення true
означає, що значення є істинним. Примус до булевого значення false
означає, що значення є хибним.
В Javascript є декілька значень, які повертають хибні значення:
if (false) // falsy
if (0) // falsy
if (null) // falsy
if (undefined) // falsy
if ("") // falsy
if (NaN) // falsy
if (-0) // falsy
Все інше - правда,
if (-1) // truthy
if ("0") // truthy
if ({}) // truthy
Наведені вище фрагменти - це добре, але краще бути явним, коли намагаєшся визначити правдивість значення. Загалом, не покладайтеся на неявний примус Javascript, навіть якщо вам здається, що ви знаєте його досконало.
Замість фрагмента коду нижче,
const counter = 2
if (counter)
Будь-який з наведених нижче способів є кращою практикою залежно від ваших вимог:
if (counter === 2)
// Or
if (typeof counter === "number")
Це пов'язано з тим, що, наприклад, ви визначаєте функцію, яка має працювати з числами:
const add = (number) => {
if (!number) new Error("Only accepts arguments of type: number")
// Your code
}
Отже, якщо викликати функцію add з 0, завжди отримаємо непередбачувану помилку:
add(0) // Error: Only accepts arguments of type: number
// Better check
const add = (number) => {
if (typeof number !== "number") new Error("Only accepts arguments of type: number")
// Your code
}
add(0) // No error
NaN
NaN
- це особливе числове значення, яке не дорівнює самому собі.
NaN === NaN // false
const notANumber = 3 * "a" // NaN
notANumber == notANumber // false
notANumber === notANumber // false
NaN
- це єдине значення Javascript, яке не дорівнює самому собі. Тому ви можете перевірити наявність NaN
, порівнявши його з самим собою.
if (notANumber !== notANumber) // true
У ECMAScript 6 введено метод перевірки NaN
, Number.isNaN
.
Number.isNaN(NaN) // true
Number.isNaN("name") // false
Остерігайтеся глобальної функції isNaN
, вона намагається примусити аргумент, перш ніж перевірити, чи є він NaN
.
isNaN("name") // true
isNaN("1") // false
Глобальну функцію isNaN
слід уникати, оскільки вона працює подібно до функції нижче:
const coerceThenCheckNaN = (val) => {
const coercedVal = Number(val)
return coercedVal !== coercedVal ? true : false
}
coerceThenCheckNaN("1a") // true
coerceThenCheckNaN("1") // false
coerceThenCheckNaN("as") // true
coerceThenCheckNaN(NaN) // true
coerceThenCheckNaN(10) // false
Оператор new
Оскільки 'new' створює екземпляр об'єкта 'Number', який порівнюється за посиланням. Значення не дорівнює посиланню на об'єкт.
3 === new Number(3) // false
3 === Number(3) // true
typeof new Number(3) // object
typeof Number(3) // number
typeof 3 // number
Це більша частина неявного примусу.
Posted on July 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.