JavaScript type coercion explained Know your - Lee-hyuna/33-js-concepts-kr GitHub Wiki

JavaScript type coercion explained Know your engines

[2018λ…„ 5μ›” 2일에 μˆ˜μ •λ¨]:이 글은 available in Russian의 글이닀. Serj Bulavyk의 λ…Έλ ₯에 λ°•μˆ˜λ₯Ό ...

νƒ€μž… λ³€ν™˜μ€ ν•œ νƒ€μž…μ—μ„œ λ‹€λ₯Έ νƒ€μž…(λ¬Έμžμ—΄μ—μ„œ 숫자, objectμ—μ„œ boolean λ“±)으둜 값을 λ³€ν™˜ ν•˜λŠ” 과정이닀. μ›μ‹œμ μ΄λ“  object이든 μ–΄λ–€ νƒ€μž…μ΄λ“  νƒ€μž… λ³€ν™˜μ˜ λŒ€μƒμ΄ λœλ‹€. 호좜 ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ›μ‹œ νƒ€μž…λ“€μ€ number, string, boolean, null, undefined, ES6에 μΆ”κ°€λœ Symbol이닀.

νƒ€μž… λ³€ν™˜μ˜ μ‹€μ§ˆμ μΈ 예λ₯Ό λ“€μžλ©΄, λŠμŠ¨ν•œ 동등 μ—°μ‚°μž a 와 b의 νƒ€μž…μ— λŒ€ν•΄ μ–΄λ–»κ²Œ μ μš©λ˜λŠ”μ§€ λ³΄μ—¬μ£ΌλŠ” JavaScript Comparison Tableλ₯Ό 보라. 이 ν‘œλŠ” == μ—°μ‚°μžκ°€ 암묡적인 νƒ€μž…μ˜ ν˜•λ³€ν™˜μœΌλ‘œ 인해 ν˜Όλˆμ„ μ£Όκ³  μ΄λŸ¬ν•œ 쑰합듀은 λͺ¨λ‘ κΈ°μ–΅ν•˜κΈ°λŠ” νž˜λ“€λ‹€. (scaryλ₯Ό ν˜Όλˆμ„ μ€€λ‹€κ³  μ˜μ—­ν•¨...) κ·Έλ ‡κ²Œ ν•  ν•„μš” μ—†κ³ , κ·Έλƒ₯ 기본적인 νƒ€μž…μ˜ νƒ€μž… λ³€ν™˜μ˜ 원리λ₯Ό 배우기만 ν•˜λ©΄ λœλ‹€.

이 글은 μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ νƒ€μž…λ³€ν™˜μ΄ μ–΄λ–»κ²Œ μž‘μš©ν•˜λŠ”μ§€ μ‹¬μΈ΅μ μœΌλ‘œ μ„€λͺ…ν•˜λ©°, ν•„μˆ˜μ μΈ μ§€μ‹μœΌλ‘œ 무μž₯ μ‹œμΌœμΌœμ£Όλ―€λ‘œ, λ‹€μŒκ³Ό 같은 ν‘œν˜„λ“€μ΄ μ–΄λ–€ 연산을 ν•˜λŠ”μ§€ μ„€λͺ…ν•˜λŠ” μžμ‹ κ°μ„ λŠλ‚„ 수 μžˆμ„ 것이닀. 기사가 끝날 λ•ŒκΉŒμ§€ λ‚˜λŠ” 닡을 보여주고 μ„€λͺ…을 ν•˜κ² λ‹€.

true + false
12 / "6"
"number" + 15 + 3
15 + 3 + "number"
[1] > null
"foo" + + "bar"
'true' == true
false == 'false'
null == ''
!!"false" == !!"true"
[β€˜x’] == β€˜x’
[] + null + 1
[1,2,3] == [1,2,3]
{}+[]+{}+[1]
!+[]+[]+![]
new Date(0) - 0
new Date(0) + 0

그래 μœ„μ˜ μ½”λ“œλ“€μ€ κ°œλ°œμžλ‘œμ„œ ν•  수 μžˆλŠ” κ½€ 어리석은 것(μ‹€μˆ˜)듀이닀. 이 μ‚¬λ‘€λ“€μ˜ 90%μ—μ„œλŠ” 암묡적인 ν˜•νƒœμ˜ νƒ€μž… λ³€ν™˜μ˜ κ°•μ œμ„±μ„ ν”Όν•˜λŠ” 것이 μ’‹μ•„. μœ„μ˜ μ½”λ“œλ“€μ€ νƒ€μž… λ³€ν™˜μ΄ μ–΄λ–»κ²Œ μž‘μš©ν•˜λŠ”μ§€ λ‹Ήμ‹ μ˜ 지식을 μ‹œν—˜ν•˜κΈ° μœ„ν•œ ν•™μŠ΅μœΌλ‘œ 해봐. μ§€λ£¨ν•˜λ©΄ wtfjs.comμ—μ„œ 더 λ§Žμ€ 예λ₯Ό 찾을 수 μžˆμ–΄.

그런데 λ•Œλ•Œλ‘œ Javascript 개발자 직책에 λŒ€ν•œ λ©΄μ ‘μœΌλ‘œ νƒ€μž… λ³€ν™˜μ— λŒ€ν•΄ μ§ˆλ¬Έμ„ 받을 수 μžˆμœΌλ‹ˆ 계속 읽어 πŸ˜„

암묡적 vs. λͺ…μ‹œμ  ν˜•λ³€ν™˜

νƒ€μž… λ³€ν™˜μ€ μ•”λ¬΅μ μ΄κ±°λ‚˜ λͺ…μ‹œμ μΌ 수 μžˆλ‹€.

κ°œλ°œμžκ°€ Number(value) 처럼 μ μ ˆν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ—¬ μœ ν˜• 간에 λ³€ν™˜ν•  μ˜λ„λ₯Ό λ‚˜νƒ€λ‚Ό λ•Œ λͺ…μ‹œμ  ν˜•λ³€ν™˜ (λ˜λŠ” νƒ€μž… μΊμŠ€νŒ…)이라고 λΆ€λ₯Έλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” νƒ€μž…μ— λŠμŠ¨ν•œ μ–Έμ–΄(weakly-typed languageλ₯Ό νƒ€μž…μ— λŠμŠ¨ν•œ 언어라고 λ²ˆμ—­ν•¨)이기 λ•Œλ¬Έμ— 값도 μžλ™μœΌλ‘œ λ‹€λ₯Έ νƒ€μž… 간에 λ³€ν™˜ 될 수 있으며, 암묡적 λ³€ν™˜μ΄λΌκ³  ν•œλ‹€. μ—°μ‚°μžλ₯Ό λ‹€λ₯Έ νƒ€μž…μ˜ 값에 μ μš©ν•  λ•Œ 주둜 λ°œμƒν•œλ‹€.

1 == null, 2/'5', null + new Date() λ˜λŠ” if (value) {…} 처럼 μ£Όλ³€ μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ value이 boolean으둜 ν˜•λ³€ν™˜μ΄ μΌμ–΄λ‚œλ‹€.

암묡적 ν˜•λ³€ν™˜μ΄ λ°œμƒν•˜μ§€ μ•Šμ€ μ—°μ‚°μžλŠ” μ—„κ²©ν•œ λ™λ“±μ—°μ‚°μžλ‘œ λΆˆλ¦¬λŠ” ===이닀. 반면 λŠμŠ¨ν•œ λ™λ“±μ—°μ‚°μžλŠ” ==을 λΉ„κ΅ν•˜κ³  ν•„μš”ν•˜λ‹€λ©΄ νƒ€μž…ν˜•λ³€ν™˜μ΄ μΌμ–΄λ‚œλ‹€.

암묡적 ν˜•λ³€ν™˜μ€ double edge sword이닀. 이것은 쒌절과 κ²°ν•¨μ˜ ν›Œλ₯­ν•œ μ›μ²œμ΄μ§€λ§Œ λ˜ν•œ νŒλ³„μ„±μ„ μžƒμ§€ μ•Šκ³  μ½”λ“œλ₯Ό 적게 μ“Έ 수 있게 ν•΄μ£ΌλŠ”λ° μœ μš©ν•œ λ©”μ»€λ‹ˆμ¦˜μ΄λ‹€.

3가지 λ³€ν™˜ νƒ€μž…

μžλ°”μŠ€ν¬λ¦½νŠΈμ—λŠ” 세가지 λ³€ν™˜ νƒ€μž…μ΄ μžˆλ‹€λŠ” 것을 μ•Œμ•Όν•œλ‹€.

  • to string
  • to boolean
  • to number

λ‘˜μ§Έλ‘œ μ›μ‹œνƒ€μž…κ³Ό object에 λŒ€ν•œ λ³€ν™˜ λ‘œμ§μ€ λ‹€λ₯΄κ²Œ μž‘μš©ν•˜μ§€λ§Œ, μ›μ‹œνƒ€μž…κ³Ό object λͺ¨λ‘ 이 세가지 λ°©λ²•μœΌλ‘œ λ³€ν™˜λ  수 μžˆλ‹€.

μ›μ‹œ νƒ€μž… λΆ€ν„° μ‹œμž‘ν•˜μž !

λ¬Έμžμ—΄ λ³€ν™˜

값을 λ¬Έμžμ—΄λ‘œ λͺ…μ‹œμ μœΌλ‘œ λ³€ν™˜ν•˜λ €λ©΄ String()을 μ‚¬μš©ν•œλ‹€. 암묡적 ν˜•λ³€ν™˜μ€ λ‹€μŒκ³Ό 같은 λ¬Έμžμ—΄μΌ λ•Œ 이진 μ—°μ‚°μž +에 μ˜ν•΄ μΌμ–΄λ‚œλ‹€.

String(123) // explicit
123 + ''    // implicit

λͺ¨λ“  μ›μ‹œ 값은 μ˜ˆμƒλŒ€λ‘œ μžμ—°μŠ€λŸ½κ²Œ λ¬Έμžμ—΄λ‘œ λ³€ν™˜λœλ‹€.

String(123)                   // '123'
String(-12.3)                 // '-12.3'
String(null)                  // 'null'
String(undefined)             // 'undefined'
String(true)                  // 'true'
String(false)                 // 'false'

Symbol conversion is a bit tricky, because it can only be converted explicitly, but not implicitly. Read more on Symbol coercion rules. Symbol λ³€ν™˜μ€ λͺ…μ‹œμ μœΌλ‘œ λ³€ν™˜ν•  수 μžˆμ„ 뿐, μ•”λ¬΅μ μœΌλ‘œλŠ” λ³€ν™˜ν•  수 μ—†κΈ° λ•Œλ¬Έμ— μ•½κ°„ κΉŒλ‹€λ‘­λ‹€.Symbol κ·œμΉ™μ— κ΄€λ ¨ν•œ 것은 https://leanpub.com/understandinges6/read/#leanpub-auto-symbol-coercion 을 ν•œλ²ˆ 읽어보라.

String(Symbol('my symbol'))   // 'Symbol(my symbol)'
'' + Symbol('my symbol')      // TypeError is thrown

Boolean λ³€ν™˜

값을 Boolean으둜 λͺ…μ‹œμ μœΌλ‘œ λ³€ν™˜ν•˜λ €λ©΄ Boolean()을 μ μš©ν•œλ‹€. μ•”μ‹œμ  ν˜•λ³€ν™˜μ€ 논리적인 λ§₯λ½μ—μ„œ λ°œμƒν•˜κ±°λ‚˜, λ…Όλ¦¬μ—°μ‚°μž( || && !)에 μ˜ν•΄ μΌμ–΄λ‚œλ‹€.

Boolean(2)          // explicit
if (2) { ... }      // implicit due to logical context
!!2                 // implicit due to logical operator
2 || 'hello'        // implicit due to logical operator

μ°Έκ³  : || 와 &&κ³Ό 같은 논리 μ—°μ‚°μžλŠ” λ‚΄λΆ€μ μœΌλ‘œ boolean ν˜•λ³€ν™˜μ„ ν•˜μ§€λ§Œ μ‹€μ œλ‘œλŠ” boolean이 μ•„λ‹ˆλ”λΌλ„ μ›λž˜μ˜ ν”Όμ—°μ‚°μžμ˜ 값을 λ°˜ν™˜ν•œλ‹€.

// returns number 123, instead of returning true
// 'hello' and 123 are still coerced to boolean internally to calculate the expression
let x = 'hello' && 123;   // x === 123

boolean λ³€ν™˜μ˜ κ²°κ³Όκ°€ true λ˜λŠ” false이 두가지 뿐이며, 거짓 같은 값듀에 λŒ€ν•œ λͺ©λ‘μ„ μ‰½κ²Œ κΈ°μ–΅ν•  수 μžˆλ‹€.

Boolean('')           // false
Boolean(0)            // false     
Boolean(-0)           // false
Boolean(NaN)          // false
Boolean(null)         // false
Boolean(undefined)    // false
Boolean(false)        // false

μœ„μ˜ μ½”λ“œλ“€μ— μ—†λŠ” 값은 object, function, Array, Date, user-defined type λ“± μ§„μ§œ 같은 true둜 λ°˜ν™˜ν•œλ‹€. Symbol은 μ°Έ 같은 값이이닀. 빈 object와 array도 μ°Έ 같은 값이닀.

Boolean({})             // true
Boolean([])             // true
Boolean(Symbol())       // true
!!Symbol()              // true
Boolean(function() {})  // true

μˆ«μžν˜•λ³€ν™˜

λͺ…μ‹œμ μœΌλ‘œ λ³€ν™˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” Boolean() 와 String()와 같이 Number()을 μ‚¬μš©ν•œλ‹€.

더 λ§Žμ€ μΌ€μ΄μŠ€μ—μ„œ μΌμ–΄λ‚˜κΈ° λ•Œλ¬Έμ— μ•”μ‹œμ  ν˜•λ³€ν™˜μ€ κΉŒλ‹€λ‘­λ‹€.

  • λΉ„κ΅μ—°μ‚°μžλ“€ (>, <, <=,>=)
  • λΉ„νŠΈμ—°μ‚°μžλ“€ ( | & ^ ~)
  • μ‚°μˆ  μ—°μ‚°μžλ“€(- + * / % ). μ°Έκ³ , 이진 μ—°μ‚°μž +λŠ” λ¬Έμžμ—΄μ„ λ§Œλ‚˜λ©΄ μˆ«μžν˜•λ³€ν™˜μ΄ μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€.
  • unary + operator
  • λŠμŠ¨ν•œ λ™λ“±μ—°μ‚°μž == (incl. !=).

μ°Έκ³  : ==은 λ‘κ°œμ˜ 비ꡐ가 λ¬Έμžμ—΄μΌ λ•Œ 숫자 ν˜•λ³€ν™˜μ€ μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€.

Number('123')   // explicit
+'123'          // implicit
123 != '456'    // implicit
4 > '5'         // implicit
5/null          // implicit
true | 0        // implicit

여기에 μ›μ‹œκ°’μ—μ„œ 숫자둜 λ³€ν™˜ν•˜λŠ” 방법은 λ‹€μŒκ³Ό κ°™λ‹€.

Number(null)                   // 0
Number(undefined)              // NaN
Number(true)                   // 1
Number(false)                  // 0
Number(" 12 ")                 // 12
Number("-12.34")               // -12.34
Number("\n")                   // 0
Number(" 12s ")                // NaN
Number(123)                    // 123

λ¬Έμžμ—΄μ„ 숫자둜 λ³€ν™˜ν•  λ•Œ 엔진은 λ¨Όμ € 백슀페이슀, \n, \t λ¬Έμžμ—΄μ„ μž˜λΌμ„œ μž˜λΌλ‚Έ λ¬Έμžμ—΄μ΄ μœ νš¨ν•œ 숫자λ₯Ό λ‚˜νƒ€λ‚΄μ§€ μ•ŠμœΌλ©΄ NaN을 λ°˜ν™˜ν•œλ‹€. λ¬Έμžμ—΄μ΄ λΉ„μ–΄ 있으면 0을 λ°˜ν™˜ν•œλ‹€.

null and undefined은 λ‹€λ₯΄κ²Œ λ‹€λ€„μ§€λŠ”λ°, null 은 0이 되고, undefined은 NaN이 λœλ‹€.

Symbols은 λͺ…μ‹œμ μœΌλ‘œλ‚˜ μ•”λ¬΅μ μœΌλ‘œ 숫자둜 λ³€ν™˜ν•  수 μ—†λ‹€. κ²Œλ‹€κ°€ TypeErrorλŠ” undefined처럼 NaN으둜 λ³€ν™˜λ˜μ–΄ 진닀. (Moreover, TypeError is thrown, instead of silently converting to NaN, like it happens for undefined.) Symbol의 λ³€ν™˜μ˜ κ·œμΉ™μ€ MDNμ—μ„œ 더 많이 λ‚˜μ™€ μžˆλ‹€.

Number(Symbol('my symbol'))    // TypeError is thrown
+Symbol('123')                 // TypeError is thrown

두가지 νŠΉλ³„ν•œ 룰이 μžˆλ‹€λŠ” 것을 κΈ°μ–΅ν•˜λΌ. 1.null λ˜λŠ” undefined에 ==을 μ μš©ν•˜λ©΄ 숫자 λ³€ν™˜μ€ μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€. null은 null λ˜λŠ” undefined에 ν•΄λ‹Ήν•˜λ©° λ‹€λ₯Έ μ–΄λ–€ 것과 같지 μ•Šλ‹€.

null == 0               // false, null is not converted to 0
null == null            // true
undefined == undefined  // true
null == undefined       // true
  1. NaN은 κ·Έ μžμ²΄μ™€λ„ 비ꡐ가 λ˜μ§€ μ•ŠλŠ”λ‹€.
if (value !== value) { console.log("we're dealing with NaN here") }

objectλ₯Ό μœ„ν•œ νƒ€μž… ν˜•λ³€ν™˜

μ§€κΈˆκΉŒμ§€ μ›μ‹œκ°’μ— λŒ€ν•œ νƒ€μž… ν˜•λ³€ν™˜μ„ μ‚΄νŽ΄λ³΄μ•˜λ‹€. λ³„λ‘œ ν₯미둭지 μ•Šμ•„.

object와 엔진이 [1] + [2,3]κ³Ό 같은 ν‘œν˜„μ΄ μžˆμ„ λ•ŒλŠ” λ¨Όμ € object을 μ›μ‹œκ°’μ„ λ³€ν™˜ν•œ λ‹€μŒ μ΅œμ’… νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•΄μ•Όν•œλ‹€. 그리고 μ—¬μ „νžˆ 숫자, λ¬Έμžμ—΄, boolean의 세가지 λ³€ν™˜ μœ ν˜•λ§Œ μžˆλ‹€.

κ°€μž₯ κ°„λ‹¨ν•œ κ²½μš°λŠ” boolean ν˜•λ³€ν™˜μ΄λ‹€. λΉ„ μ›μ‹œκ°’μ€ 항상 object이든 배열이든 간에 true에 μ˜ν•΄ λ³€ν™˜μ΄ λœλ‹€.

objectλŠ” 숫자 및 λ¬Έμžμ—΄ λ³€ν™˜μ„ λͺ¨λ‘ λ‹΄λ‹Ήν•˜λŠ” λ‚΄λΆ€ [[ToPrimitive]]을 톡해 μ›μ‹œ μƒνƒœλ‘œ λ³€ν™˜λœλ‹€.

λ‹€μŒμ€ [[ToPrimitive]]의 λ©”μ†Œλ“œμ— μ˜ν•œ μˆ˜λ„ μ½”λ“œμ΄λ‹€.

function ToPrimitive(input, preferredType){
  
  switch (preferredType){
    case Number:
      return toNumber(input);
      break;
    case String:
      return toString(input);
      break
    default:
      return toNumber(input);  
  }
  
  function isPrimitive(value){
    return value !== Object(value);
  }

  function toString(){
    if (isPrimitive(input.toString())) return input.toString();
    if (isPrimitive(input.valueOf())) return input.valueOf();
    throw new TypeError();
  }

  function toNumber(){
    if (isPrimitive(input.valueOf())) return input.valueOf();
    if (isPrimitive(input.toString())) return input.toString();
    throw new TypeError();
  }
}

js-to-primitive-internal.js

[[ToPrimitive]]은 input valueκ³Ό preferred type은 Number 와 String으둜 값이 μ „λ‹¬λ˜λ©° preferredType은 쑰건에 optionalν•œ 뢀뢄이닀.

숫자 ν˜•λ³€ν™˜κ³Ό λ¬Έμžμ—΄ λ³€ν™˜ λͺ¨λ‘ input ojbect의 valueOf와 toString이 두가지 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ”λ°, 두가지 λ©”μ„œλ“œλŠ” λͺ¨λ‘ Object.prototype에 λͺ…μ‹œλ˜μ–΄ μžˆμ–΄ Date, Array λ“± νŒŒμƒλœ νƒ€μž…μ—μ„œ μ‚¬μš©ν•  수 μžˆλ‹€.

μ•„λž˜λŠ” 일반적인 μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€.

  1. input이 이미 μ›μ‹œμ μ΄λΌλ©΄, 아무 것도 ν•˜μ§€ μ•Šκ³  λ°˜ν™˜λœλ‹€.

  2. input.toString()이 호좜되면, κ·Έ κ²°κ³Όκ°€ μ›μ‹œμ μ΄λΌλ©΄ λ°˜ν™˜ν•œλ‹€.

  3. input.valueOf()이 호좜되면, κ·Έ κ²°κ³Όκ°€ μ›μ‹œμ μ΄λΌλ©΄ λ°˜ν™˜ν•œλ‹€.

  4. input.toString() μ΄λ‚˜ input.valueOf()이 μ›μ‹œμ μ΄μ§€ μ•Šλ‹€λ©΄ TypeErrorλ₯Ό λ°˜ν™˜ν•˜λΌ.

숫자 ν˜•λ³€ν™˜μ€ μš°μ„  valueOf (3)을 ν˜ΈμΆœν•˜κ³ , toString (2) ν˜ΈμΆœν•œλ‹€. λ¬Έμžν˜•λ³€ν™˜μ€ toString (2)을 ν˜ΈμΆœν•œ ν›„ valueOf (3) ν˜ΈμΆœν•˜μ—¬ 숫자 λ³€ν™˜κ³Ό λ¬Έμžν˜• λ³€ν™˜μ€ λ°˜λŒ€μ΄λ‹€.

λŒ€λΆ€λΆ„μ˜ 빌트인된 νƒ€μž…μ€ valueOf이 μ—†κ±°λ‚˜ thisλŠ” objectλ₯Ό λ°˜ν™˜ν•˜λŠ” valueOfκ°€ μ—†μ–΄μ„œ μ›μ‹œμ μΈ 것이 μ•„λ‹ˆλ―€λ‘œ λ¬΄μ‹œλ˜κ³  μž‡λ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— μˆ«μžμ™€ λ¬Έμžμ—΄ λ³€ν™˜ λͺ¨λ‘ toString()이라고 λΆ€λ₯΄λŠ” κ²ƒμœΌλ‘œ 끝날 수 μžˆλ‹€.

λ‹€λ₯Έ μ—°μ‚°μžλŠ” preferredTypeνŒŒλΌλ―Έν„°μ˜ 도움을 λ°›μ•„ 숫자 λ˜λŠ” λ¬Έμžμ—΄ λ³€ν™˜μ„ μΌμœΌν‚¬ 수 μžˆλ‹€. κ·ΈλŸ¬λ‚˜ ==와 이진 μ—°μ‚°μž+λŠ” default λ³€ν™˜ μƒνƒœλ₯Ό μΌμœΌν‚€λŠ” 두가지 κ²½μš°μ΄λ‹€. 이 경우 λ¬Έμžμ—΄ λ³€ν™˜μ„ ν•˜λŠ” Date을 μ œμ™Έν•˜κ³  λŒ€λΆ€λΆ„μ˜ λ‚΄μž₯된 νƒ€μž…μ€ 숫자 λ³€ν™˜μ„ default둜 κ°€μ •ν•œλ‹€.

Date의 λ³€ν™˜μ˜ μ˜ˆμ΄λ‹€.

let d = new Date();

// get string representation
let str = d.toString();  // 'Wed Jan 17 2018 16:15:42'

// get numeric representation, num of milliseconds since Unix epoch
let num = d.valueOf();   // 1516198542525

// compare with a string representation
// true because d is converted to same string
console.log(d == str);   // true

// compare with numeric representation
// false, because d is not converted to a number via valueOf()
console.log(d == num);   // false

// Result is 'Wed Jan 17 2018 16:15:42Wed Jan 17 2018 16:15:42'
// '+' same to '==' triggers default conversion mode
console.log(d + d);

// Result is 0, since '-' operator explicitly triggers numeric conversion, not a default one
console.log(d - d);

date-object-to-primitive-conversion.js

objectμ—μ„œ μ›μ‹œκ°’μœΌλ‘œ ν˜•λ³€ν™˜ λ‘œμ§μ— hook에 μ˜ν•΄ toString() κ³Ό valueOf() λ©”μ„œλ“œμ˜ 기본값을 overrideν•  수 μžˆλ‹€.

var obj = {
  prop: 101,
  toString(){
    return 'Prop: ' + this.prop;
  },
  valueOf() {
    return this.prop;
  }
};

console.log(String(obj));  // 'Prop: 101'
console.log(obj + '')      // '101'
console.log(+obj);         //  101
console.log(obj > 100);    //  true
view raw

es5-toprimitive-hooks.js obj + β€˜β€™ κ°€ μ–΄λ–»κ²Œ β€˜101’을 λ¬Έμžμ—΄λ‘œ λ°˜ν™˜ν•˜λŠ”μ§€ μ£Όλͺ©ν•˜λΌ. + μ—°μ‚°μžλŠ” κΈ°λ³Έ λ³€ν™˜ λͺ¨λ“œλ₯Ό 일으켜 μ•žμ„œ λ§ν•œ κ²ƒμ²˜λŸΌ Object은 숫자 ν˜•λ³€ν™˜μ„ λ””ν΄νŠΈλ‘œ κ°€μ •ν•˜μ—¬ toString()이 μ•„λ‹Œ valueOf() λ©”μ„œλ“œλ₯Ό λ¨Όμ € μ‚¬μš©ν•œλ‹€.

ES6의 Symbol.toPrimitive

ES5μ—μ„œλŠ” toString 와 valueOf λ©”μ„œλ“œ overriding에 μ˜ν•΄ μ˜€λΈŒμ νŠΈμ—μ„œ μ›μ‹œκ°’μœΌλ‘œ hook ν•  수 μžˆλ‹€.

ES6μ—μ„œλŠ” object에 [Symbol.toPrimtive]λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ‚΄λΆ€ [[ToPrimitive]]의 루틴을 μ™„μ „νžˆ λŒ€μ²΄ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

class Disk {
  constructor(capacity){
    this.capacity = capacity;
  }

  [Symbol.toPrimitive](hint){
    switch (hint) {
      case 'string':
        return 'Capacity: ' + this.capacity + ' bytes';

      case 'number':
        // convert to KiB
        return this.capacity / 1024;

      default:
        // assume numeric conversion as a default
        return this.capacity / 1024;
    }
  }
}

// 1MiB disk
let disk = new Disk(1024 * 1024);

console.log(String(disk))  // Capacity: 1048576 bytes
console.log(disk + '')     // '1024'
console.log(+disk);        // 1024
console.log(disk > 1000);  // true
view raw

es6-symbol-toprimitive.js

예

μ΄λŸ¬ν•œ 이둠이 μ μš©λ˜μ–΄ μžˆλŠ” λ‹€μŒμ˜ 예λ₯Ό λ“€μ–΄λ³΄μž.

true + false             // 1
12 / "6"                 // 2
"number" + 15 + 3        // 'number153'
15 + 3 + "number"        // '18number'
[1] > null               // true
"foo" + + "bar"          // 'fooNaN'
'true' == true           // false
false == 'false'         // false
null == ''               // false
!!"false" == !!"true"    // true
['x'] == 'x'             // true 
[] + null + 1            // 'null1'
[1,2,3] == [1,2,3]       // false
{}+[]+{}+[1]             // '0[object Object]1'
!+[]+[]+![]              // 'truefalse'
new Date(0) - 0          // 0
new Date(0) + 0          // 'Thu Jan 01 1970 02:00:00(EET)0'

μ•„λž˜μ—μ„œλŠ” 각각의 ν‘œν˜„μ— λŒ€ν•œ μ„€λͺ…을 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄μ§„μ—°μ‚°μž +λŠ” true와 false의 숫자 λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€.

true + false
==> 1 + 0
==> 1

μ‚°μˆ μ—°μ‚°μž /λŠ” λ¬Έμžμ—΄ '6' 의 숫자 λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€.

12 / '6'
==> 12 / 6
==>> 2

μ—°μ‚°μž +λŠ” left-to-right으둜 연관이 있기 λ•Œλ¬Έμ—(μ™Όμͺ½μ—μ„œ 였λ₯Έμͺ½μœΌλ‘œ 연산을 μ§„ν–‰ν•œλ‹€ λŠλ‚Œ...) "number" + 15이 λ¨Όμ € λ‚˜μ˜¨λ‹€. ν•œ ν”Όμ—°μ‚°μžλŠ” λ¬Έμžμ—΄μ΄κΈ° λ•Œλ¬Έμ— +μ—°μ‚°μžλŠ” 숫자 15에 λŒ€ν•œ λ¬Έμžμ—΄ λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€. λ‘λ²ˆμ§Έ 단계 expression은 "number15" + 3도 μ²«λ²ˆμ§Έμ™€ μœ μ‚¬ν•˜κ²Œ 비ꡐ가 λœλ‹€.

β€œnumber” + 15 + 3 
==> "number15" + 3 
==> "number153"

15 + 3이 λ¨Όμ € 비ꡐ가 λœλ‹€. 두 ν”Όμ—°μ‚°μžλŠ” λͺ¨λ‘ 숫자인 만큼 κ°•μš”ν•  ν•„μš”λŠ” μ „ν˜€ μ—†λ‹€. λ‘λ²ˆμ§Έ 단계 μ—μ„œλŠ” 18 + 'number'이 λ¨Όμ € 비ꡐ가 되며, ν•˜λ‚˜μ˜ ν”Όμ—°μ‚°μžλŠ” λ¬Έμžμ—΄μ΄κΈ° λ•Œλ¬Έμ— λ¬Έμžμ—΄ λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€.

15 + 3 + "number" 
==> 18 + "number" 
==> "18number"

비ꡐ μ—°μ‚°μž >은 [1] κ³Ό null둜 숫자 λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€.

[1] > null
==> '1' > 0
==> 1 > 0
==> true

Unary + μ—°μ‚°μžλŠ” μ΄μ§„μ—°μ‚°μž +보닀 μš°μ„ μˆœμœ„κ°€ λ†’λ‹€. κ·Έλž˜μ„œ +'bar'κ°€ λ¨Όμ € 비ꡐ가 λœλ‹€. Unary +λŠ” λ¬Έμžμ—΄ 'bar'의 숫자 λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€.λ¬Έμžμ—΄μ΄ μœ νš¨ν•œ 숫자λ₯Ό λ‚˜νƒ€λ‚΄μ§€ λͺ»ν•˜κΈ° λ•Œλ¬Έμ— κ²°κ³ΌλŠ” NaN이닀. λ‘λ²ˆμ§Έ λ‹¨κ³„μ—μ„œλŠ” 'foo' + NaN이 비ꡐ가 λœλ‹€.

"foo" + + "bar" 
==> "foo" + (+"bar") 
==> "foo" + NaN 
==> "fooNaN"

== 은 μˆ«μžν˜• λ³€ν™˜μ„ μΌμœΌν‚€κ³ , λ¬Έμžμ—΄ 'true'을 μ „λΆ€ NaN으둜 λ³€ν™˜λ˜μ–΄ boolean true은 1둜 λ³€ν™˜λœλ‹€.

'true' == true
==> NaN == 1
==> false
false == 'false'   
==> 0 == NaN
==> false

==은 보톡 숫자 λ³€ν™˜μ„ μΌμœΌν‚€μ§€λ§Œ,null의 κ²½μš°λŠ” 그렇지 μ•Šλ‹€. null은 null λ˜λŠ” undefined 이고, λ‹€λ₯Έ μ–΄λ–€ 것과도 같지 μ•Šλ‹€.

null == ''
==> false

!! μ—°μ‚°μžλŠ” 'true' 와 'false' 두가지 λ¬Έμžμ—΄μ„ true둜 λ³€ν™˜ν•˜λŠ”λ°, μ΄λŠ” λΉ„μ–΄ μžˆμ§€ μ•Šμ€ λ¬Έμžμ—΄μ΄κΈ° λ•Œλ¬Έμ΄λ‹€. κ·ΈλŸ¬κ³ λ‚˜μ„œ ==은 trueλΌλŠ” 두 개의 boolean은 μ•„λ¬΄λŸ° ν˜•λ³€ν™˜μ—†μ΄ κ°™μ€μ§€λ§Œ μ²΄ν¬ν•œλ‹€.

!!"false" == !!"true"  
==> true == true
==> true

== μ—°μ‚°μžλŠ” λ°°μ—΄μ˜ 숫자 λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€. λ°°μ—΄μ˜ valueOf() λ©”μ†Œλ“œλŠ” λ°°μ—΄ 자체λ₯Ό λ°˜ν™˜ν•˜μ—¬ μ›μ‹œμ μΈ 것이 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— λ¬΄μ‹œλ˜κ³  μžˆλ‹€. λ°°μ—΄μ˜ toString() 은 ['x'] 을 'x' λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•œλ‹€.

['x'] == 'x'  
==> 'x' == 'x'
==>  true

+ μ—°μ‚°μžλŠ” []에 λŒ€ν•œ 숫자 λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€. λ°°μ—΄μ˜ valueOf() λ©”μ„œλ“œλŠ” 기본이 μ•„λ‹Œ λ°°μ—΄ 자체λ₯Ό λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— λ¬΄μ‹œλ˜κ³  μžˆλ‹€. λ°°μ—΄μ˜ toString은 빈 λ¬Έμžμ—΄μ„ λ°˜ν™˜ν•œλ‹€.

λ‘λ²ˆμ§Έ λ‹¨κ³„μ—μ„œλŠ” '' + null + 1으둜 비ꡐ가 λœλ‹€.

[] + null + 1  
==>  '' + null + 1  
==>  'null' + 1  
==> 'null1'

논리적인 || 와 && μ—°μ‚°μžλŠ” boolean으둜 ν˜•λ³€ν™˜μ΄ λ˜μ§€λ§Œ μ›λž˜μ˜ ν”Όμ—°μ‚°μžλŠ” λ°˜ν™˜ν•˜μ§€ μ•Šλ„λ‘ ν•œλ‹€. 0은 거짓 같은 값인 반면 '0'은 μ°Έ 같은 값이닀. μ™œλƒν•˜λ©΄ 빈 λ¬Έμžμ—΄μ΄ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ΄λ‹€. {}의 빈 object은 μ—­μ‹œ μ°Έ 같은 값이닀.

0 || "0" && {}  
==>  (0 || "0") && {}
==> (false || true) && true  // internally
==> "0" && {}
==> true && true             // internally
==> {}

두 ν”Όμ—°μ‚°μžμ˜ νƒ€μž…μ΄ λ™μΌν•˜κΈ° λ•Œλ¬Έμ— ν˜•λ³€ν™˜μ€ ν•„μš”ν•˜μ§€ μ•ŠλŠ”λ‹€. ==은 object의 동일성이 μ•„λ‹ˆλΌ object의 identity을(my think's ... object의 identityλž€ λ¬΄μ—‡μΌκΉŒ...) ν™•μΈν•˜λŠ” 것과 두 배열이 μ„œλ‘œ λ‹€λ₯Έ 경우이기 λ•Œλ¬Έμ— κ·Έ κ²°κ³ΌλŠ” false이닀.

[1,2,3] == [1,2,3]
==>  false

λͺ¨λ“  ν”Όμ—°μ‚°μžλŠ” μ›μ‹œκ°’μ΄ μ•„λ‹ˆλ―€λ‘œ +λŠ” κ°€μž₯ μ™Όμͺ½μ— μžˆλŠ” 숫자 λ³€ν™˜μœΌλ‘œ λΆ€ν„° μ‹œμž‘ν•œλ‹€. Object’s 와 Array’s의 valueOf λ©”μ„œλ“œλŠ” object자체λ₯Ό λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— λ¬΄μ‹œλ˜κ³  μžˆλ‹€. toString()은 fallback으둜 μ‚¬μš©λœλ‹€. μ—¬κΈ°μ„œ trick은 첫번째둜 {}은 문자 κ·ΈλŒ€λ‘œκ°€ μ•„λ‹ˆλΌ 블둝 μ„ μ–Έλ¬Έμ²˜λŸΌ 여겨져 λ¬΄μ‹œλœλ‹€λŠ” 것이닀. κ·Έλž˜μ„œ λΉ„κ΅λŠ” toString() λ©”μ†Œλ“œλ₯Ό 톡해 빈 λ¬Έμžμ—΄λ‘œ λ³€ν™˜λœ λ‹€μŒ +[] μ‹μ—μ„œ μ‹œμž‘λ˜μ–΄ 0으둜 λ³€ν™˜λœλ‹€.

{}+[]+{}+[1]
==> +[]+{}+[1]
==> 0 + {} + [1]
==> 0 + '[object Object]' + [1]
==> '0[object Object]' + [1]
==> '0[object Object]' + '1'
==> '0[object Object]1'

이것은 μ—°μ‚°μž μš°μ„  μˆœμœ„μ— 따라 μ°¨κ·Όμ°¨κ·Ό μ„€λͺ…ν•˜λŠ” 것이 μ’‹λ‹€.

!+[]+[]+![]  
==> (!+[]) + [] + (![])
==> !0 + [] + false
==> true + [] + false
==> true + '' + false
==> 'truefalse'

- μ—°μ‚°μžλŠ” 숫자 ν˜•λ³€ν™˜μ„ μΌμœΌν‚¨λ‹€. Date. Date.valueOf()은 μœ λ‹‰μŠ€ 이후 수 λ°€λ¦¬μ΄ˆλ₯Ό λ°˜ν™˜ν•œλ‹€.

new Date(0) - 0
==> 0 - 0
==> 0

+ μ—°μ‚°μžλŠ” 기본적인 λ³€ν™˜μ„ μΌμœΌν‚€κ³ , DateλŠ” λ¬Έμžμ—΄ ν˜•λ³€ν™˜μ„ 기본으둜 κ°€μ •ν•˜κΈ° λ•Œλ¬Έμ— valueOf() κ°€ μ•„λ‹ˆλΌ toString() λ©”μ„œλ“œκ°€ μ‚¬μš©λœλ‹€.

new Date(0) + 0
==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)' + 0
==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)0'

참고 자료

Nicholas C. Zakasκ°€ μ“΄ Understanding ES6 책이 κ°€μž₯ 잘 μ“°μ—¬μ‘Œλ‹€κ³  μΆ”μ²œν•œλ‹€. 이 책은 λ„ˆλ¬΄ 높은 레벨이 μ•„λ‹ˆλΌ ν›Œλ₯­ν•œ ES6 ν•™μŠ΅ 자료둜 내싀을 λ„ˆλ¬΄ 많이 파고 듀지 μ•ŠλŠ”λ‹€.

Axel Rauschmayer이 μ“΄ SpeakingJS은 ES5λ₯Ό μ΅νžˆκΈ°μ— μ’‹λ‹€.

(Russian) Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ ΡƒΡ‡Π΅Π±Π½ΠΈΠΊ Javascriptβ€Šβ€”β€Šhttps://learn.javascript.ru/. 이 것은 νƒ€μž… ν˜•λ³€ν™˜μ— λŒ€ν•œ 두 νŽ˜μ΄μ§€κ°€ μžˆλ‹€.

JavaScript Comparison Tableβ€Šβ€”β€Šhttps://dorey.github.io/JavaScript-Equality-Table/

wtfjsβ€Šβ€”β€Šμž‘μ€ μ½”λ“œλ“€μ„ μ œκ³΅ν•¨β€Šβ€”β€Šhttps://wtfjs.com/

λ²ˆμ—­ 원문 : https://medium.freecodecamp.org/js-type-coercion-explained-27ba3d9a2839

⚠️ **GitHub.com Fallback** ⚠️