Неявне приведення типів у Javascript (Implicit Coercion)

betelgeuseas

Andrew

Posted on July 30, 2023

Неявне приведення типів у Javascript (Implicit Coercion)

Неявний примус у 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Випадок для оператора +

Оператор +, на відміну від інших математичних операторів, виконує дві функції:

  1. Математичне додавання;
  2. Конкатенація рядків.

Коли операндом оператора + є рядок, 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"
Enter fullscreen mode Exit fullscreen mode

Об'єкти

Більшість перетворень об'єктів Javascript зазвичай призводять до [object Object].

"name" + {} // "name[object Object]"
Enter fullscreen mode Exit fullscreen mode

Кожен об'єкт JavaScript успадковує метод toString, який викликається щоразу, коли об'єкт потрібно перетворити в рядок. Значення, що повертається методом toString, використовується для таких операцій, як конкатенація рядків і математичні вирази.

const foo = {}
foo.toString() // [object Object]

const baz = {
  toString: () => "I'm object baz"
}

baz + "!" // "I'm object baz!"
Enter fullscreen mode Exit fullscreen mode

Якщо це математичний вираз, 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
Enter fullscreen mode Exit fullscreen mode

Об'єкти масивів

Успадкований метод 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
Enter fullscreen mode Exit fullscreen mode

Отже, коли ви передаєте масив, де очікується рядок, 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
Enter fullscreen mode Exit fullscreen mode

True, False та ""

Number(true) // 1
Number(false) // 0
Number("") // 0

4 + true // 5
3 * false // 0
3 * "" // 0
3 + "" // "3"
Enter fullscreen mode Exit fullscreen mode

valueOf метод

Також можна визначити метод valueOf, який буде використовуватися Javascript щоразу, коли ви передаєте об'єкт, в якому очікується рядкове або числове значення.

const foo = {
  valueOf: () => 3
}

3 + foo // 6
3 * foo // 9
Enter fullscreen mode Exit fullscreen mode

Якщо на об'єкті визначено обидва методи toString і valueOf, Javascript використовує метод valueOf.

const bar = {
  toString: () => 2,
  valueOf: () => 5
}

"sa" + bar // "sa5"
3 * bar // 15
2 + bar // 7
Enter fullscreen mode Exit fullscreen mode

Метод valueOf призначений для об'єктів, які мають представляти числове значення.

const two = new Number(2)

two.valueOf() // 2
Enter fullscreen mode Exit fullscreen mode

Брехня і правда

Кожному значенню 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
Enter fullscreen mode Exit fullscreen mode

Все інше - правда,

if (-1) // truthy
if ("0") // truthy
if ({}) // truthy
Enter fullscreen mode Exit fullscreen mode

Наведені вище фрагменти - це добре, але краще бути явним, коли намагаєшся визначити правдивість значення. Загалом, не покладайтеся на неявний примус Javascript, навіть якщо вам здається, що ви знаєте його досконало.
Замість фрагмента коду нижче,

const counter = 2

if (counter)
Enter fullscreen mode Exit fullscreen mode

Будь-який з наведених нижче способів є кращою практикою залежно від ваших вимог:

if (counter === 2)

// Or

if (typeof counter === "number")
Enter fullscreen mode Exit fullscreen mode

Це пов'язано з тим, що, наприклад, ви визначаєте функцію, яка має працювати з числами:

const add = (number) => {
  if (!number) new Error("Only accepts arguments of type: number")
  // Your code
}
Enter fullscreen mode Exit fullscreen mode

Отже, якщо викликати функцію 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
Enter fullscreen mode Exit fullscreen mode

NaN

NaN - це особливе числове значення, яке не дорівнює самому собі.

NaN === NaN // false

const notANumber = 3 * "a" // NaN

notANumber == notANumber // false
notANumber === notANumber // false
Enter fullscreen mode Exit fullscreen mode

NaN - це єдине значення Javascript, яке не дорівнює самому собі. Тому ви можете перевірити наявність NaN, порівнявши його з самим собою.

if (notANumber !== notANumber) // true
Enter fullscreen mode Exit fullscreen mode

У ECMAScript 6 введено метод перевірки NaN, Number.isNaN.

Number.isNaN(NaN) // true
Number.isNaN("name") // false
Enter fullscreen mode Exit fullscreen mode

Остерігайтеся глобальної функції isNaN, вона намагається примусити аргумент, перш ніж перевірити, чи є він NaN.

isNaN("name") // true
isNaN("1") // false
Enter fullscreen mode Exit fullscreen mode

Глобальну функцію 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
Enter fullscreen mode Exit fullscreen mode

Оператор new

Оскільки 'new' створює екземпляр об'єкта 'Number', який порівнюється за посиланням. Значення не дорівнює посиланню на об'єкт.

3 === new Number(3) // false
3 === Number(3) // true

typeof new Number(3) // object
typeof Number(3) // number
typeof 3 // number
Enter fullscreen mode Exit fullscreen mode

Це більша частина неявного примусу.

💖 💪 🙅 🚩
betelgeuseas
Andrew

Posted on July 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related