what you need to know about javascripts implicit coercion - Lee-hyuna/33-js-concepts-kr GitHub Wiki

Javascript의 암묡적 ν˜• λ³€ν™˜(Implicit coercion)에 κ΄€ν•˜μ—¬

원문: What you need to know about Javascript's Implicit Coercion

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ 암묡적 ν˜• λ³€ν™˜μ€ 예기치 λͺ»ν•œ κ°’ νƒ€μž…μ„ κΈ°λŒ€λ˜λŠ” νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜λ €κ³  μ‹œλ„ν•˜λŠ” 것을 λ§ν•œλ‹€. λ”°λΌμ„œ λ¬Έμžμ—΄ 등을 κΈ°λŒ€ν•˜λŠ” 곳에 μ˜¬λ°”λ₯Έ λ¬Έμžμ—΄μ„, 숫자λ₯Ό κΈ°λŒ€ν•˜λŠ” 곳에 μ˜¬λ°”λ₯Έ 숫자λ₯Ό 전달해 쀄 수 μžˆλ‹€. 이것은 μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ νŠΉμ§• 쀑 ν•˜λ‚˜μ΄λ‹€.

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

숫자 κ³„μ‚°μ—μ„œ μˆ«μžκ°€ μ•„λ‹Œ κ°’

String

μ—°μ‚°μž(-, *, /, %)κ°€ ν¬ν•¨λœ 숫자 κ³„μ‚°μ—μ„œ ν”Όμ—°μ‚°μžλ‘œ λ¬Έμžμ—΄μ„ μ „λ‹¬ν•˜κ²Œ 되면, λ¬Έμžμ—΄μ— λŒ€ν•œ 숫자 λ³€ν™˜ ν”„λ‘œμ„ΈμŠ€λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ— λ‚΄μž₯된 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

λ”ν•˜κΈ° μ—°μ‚°μžμ— λŒ€ν•œ μ˜ˆμ‹œ

+ μ—°μ‚°μžλŠ” λ‹€λ₯Έ μˆ˜ν•™ μ—°μ‚°μžμ™€ λ‹€λ₯΄κ²Œ λ‘κ°€μ§€μ˜ κΈ°λŠ₯을 μˆ˜ν–‰ν•œλ‹€.

  1. μˆ˜ν•™μ  λ§μ…ˆ
  2. λ¬Έμžμ—΄μ„ μ—°κ²°(concatenation)

λ¬Έμžμ—΄μ΄ + μ—°μ‚°μžμ˜ ν”Όμ—°μ‚°μžμΌ 경우, μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” λ¬Έμžμ—΄μ„ 숫자둜 λ³€ν™˜ν•˜λŠ” λŒ€μ‹  숫자λ₯Ό λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•œλ‹€.

// λ¬Έμžμ—΄λ‘œ μ—°κ²°
1 + "2" // "12"
1 + "js" // "1js"

// λ§μ…ˆ
1 + 2 // 3
1 + 2 + 1 // 4

// λ§μ…ˆ ν›„ μ—°κ²°
1 + 2 + "1" // "31"
(1 + 2) + "1" // "31"

// λͺ¨λ‘ μ—°κ²°
1 + "2" + 1 // "121"
(1 + "2") + 1 // "121"

Object

λŒ€λΆ€λΆ„μ˜ μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ Object λ³€ν™˜μ€ λŒ€κ²Œ [object Object]둜 λ³€ν™˜λœλ‹€. μ˜ˆμ‹œλ₯Ό 보자.

"name" + {} // "name[object Object]

λͺ¨λ“  μžλ°”μŠ€ν¬λ¦½νŠΈ ObjectλŠ” toString λ©”μ„œλ“œλ₯Ό 상속 λ°›κΈ° λ•Œλ¬Έμ— Objectκ°€ String으둜 ν˜• λ³€ν™˜μ΄ κ°€λŠ₯ν•˜λ‹€. toString λ©”μ„œλ“œλ‘œ λ°˜ν™˜λœ 값은 λ¬Έμžμ—΄ μ—°κ²°, μˆ˜ν•™μ  계산과 같은 μž‘μ—…μ— μ‚¬μš©λœλ‹€.

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

const baz = {
  toString: () => "I'm object baz" // toString을 μž¬μ •μ˜
}

baz + "!" // "I'm object baz!" (toString이 ν˜ΈμΆœλ˜μ–΄ λ¬Έμžμ—΄ μ—°κ²°)

μˆ˜ν•™μ  ν‘œν˜„μ‹μΌ λ•Œ μžλ°”μŠ€ν¬λ¦½λŠ” toString의 λ°˜ν™˜ 값을 숫자둜 λ³€ν™˜ν•˜λ €κ³  ν•œλ‹€.

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 (숫자둜 λ³€ν™˜)

Array

배열에 μƒμ†λœ toString λ©”μ„œλ“œλŠ” Object와 λ™μž‘μ΄ μ•½κ°„ λ‹€λ₯΄λ‹€. λ°°μ—΄μ˜ 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

λ”°λΌμ„œ λ¬Έμžμ—΄λ‘œ κΈ°λŒ€λ˜λŠ” μ‹μ—μ„œ 배열을 μ „λ‹¬ν•˜κ²Œ 되면, μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” λ°°μ—΄μ˜ toString λ©”μ„œλ“œμ˜ λ°˜ν™˜ κ°’κ³Ό μ—°κ²°ν•œλ‹€. 결과둜 숫자λ₯Ό κΈ°λŒ€ν•  경우 λ°˜ν™˜ 값을 숫자둜 λ³€ν™˜ν•˜λ €κ³  μ‹œλ„ν•œλ‹€.

4 * [] // 0 ([].toString => '', Number('') => 0)
4 / [2] // 2

//similar to
4 * Number([].toString())
4 * Number("")
4 * 0

//

4 / Number([2].toString())
4 / Number("2")
4 / 2

True, False and ""

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

4 + true // 5
3 * false // 0
3 * "" // 0
3 + "" // "3"

valueOf λ©”μ„œλ“œ

toString λ©”μ„œλ“œ 이외에도 μžλ°”μŠ€ν¬λ¦½νŠΈκ°€ Objectκ°€ λ¬Έμžμ—΄μ΄λ‚˜ 숫자 κ°’μœΌλ‘œ λ°˜ν™˜ 되기λ₯Ό κΈ°λŒ€ν• λ•Œ μ‚¬μš©λ˜λŠ” valueOf λ©”μ„œλ“œλ„ μ •μ˜ ν•  수 μžˆλ‹€.

valueOf() λ©”μ„œλ“œλŠ” νŠΉμ • 객체의 μ›μ‹œ 값을 λ°˜ν™˜ν•œλ‹€.

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

3 + foo // 6
3 * foo // 9

toStringκ³Ό valueOf λ©”μ„œλ“œκ°€ λͺ¨λ‘ Object에 μ •μ˜λœ 경우 μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” valueOf λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€.

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

"sa" + bar // "sa5"
3 * bar // 15
2 + bar // 7

valueOf λ©”μ„œλ“œλŠ” 숫자 값을 λ‚˜νƒ€λ‚΄μ•Ό ν•˜λŠ” Objectλ₯Ό μœ„ν•΄ λ§Œλ“€μ–΄μ‘Œλ‹€.

const two = new Number(2)

two.valueOf() // 2

Falsy and Truthy

λͺ¨λ“  μžλ°”μŠ€ν¬λ¦½νŠΈ 값은 μ°Έ(true) λ˜λŠ” 거짓(false)으둜 ν˜• λ³€ν™˜λ  수 μžˆλ‹€. boolean true에 λŒ€ν•œ ν˜• λ³€ν™˜μ€ 값이 trutyλΌλŠ” 것을 μ˜λ―Έν•œλ‹€. boolean false에 λŒ€ν•œ ν˜•λ³€ν™”λŠ” falsyμ΄λΌλŠ” 것을 μ˜λ―Έν•œλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈμ—λŠ” falsy 값을 λ°˜ν™˜ν•˜λŠ” 값듀이 μ‘΄μž¬ν•œλ‹€.

  1. false
  2. 0
  3. null
  4. undefined
  5. ""
  6. NaN
  7. -0

이 외에 λ‚˜λ¨Έμ§„ 값듀은 truthy 값을 λ°˜ν™˜ν•œλ‹€.

if (-1) // truthy
if ("0") // truthy
if ({}) // truthy

μœ„μ˜ μ˜ˆμ‹œλ“€μ€ μ μ ˆν•΄ 보인닀. κ·ΈλŸ¬λ‚˜ κ°’μ˜ 진싀성을 κ²°μ •ν•˜λ €κ³  할땐 μ’€ 더 λͺ…μ‹œμ μœΌλ‘œ μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€. 기본적으둜 μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μ•”μ‹œμ  ν˜•λ³€ν™˜μ— μ˜μ‘΄ν•˜λ©΄ μ•ˆλœλ‹€. 비둝 μ•”μ‹œμ  ν˜•λ³€ν™˜μ„ μ œλŒ€λ‘œ μ•Œκ³  μžˆλ‹€κ³  생각해도 말이닀. μ•„λž˜μ™€ 같이 μ‚¬μš©ν•˜μž.

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)), μ˜λ„ν•˜μ§€ μ•Šμ€ μ—λŸ¬κ°€ 계속 λ°œμƒν•  것이닀.

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은 μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ 자기 μžμ‹ κ³Ό 같지 μ•Šμ€ μœ μΌν•œ 값이닀. λ”°λΌμ„œ NaN을 κ·Έ μžμ²΄μ™€ λΉ„κ΅ν•˜μ—¬ 확인할 수 μžˆλ‹€.

if (notANumber !== notANumber) // true

ES6μ—μ„œ 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
⚠️ **GitHub.com Fallback** ⚠️