암묵적 타입 변환(implicit coercion)

EJ·2020년 12월 2일
2

JavaScript 개념

목록 보기
4/13

자바스크립트의 암묵적 타입 변환(implicit coercion)

자바스크립트의 암묵적인 타입 변환(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

숫자 표현식에서 숫자가 아닌 값

문자열

사용자가 숫자 표현식에서 문자열(-, *, /, %을 포함한 문자열)을 피연산자로 넘겼을 때마다 숫자의 암묵적 타입변환 프로세스는 문자열을 인자로 자바스크립트 내부에 내장된 Number함수를 불러오는 것과 비슷하다. 이것은 꽤나 직관적이다. 숫자 문자(Numeric Characters)를 가졌다면 이떤 문자열이라도 동등한 숫자로 바뀐다. 하지만 문자열에 숫자가 아닌 것(Non-Numeric Characters)이 포함되어 있으면 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("la") // NaN
Number("one") // NaN
Nuber("text") // NaN

+연산자

+ 연산자는 다른 수학적 연산자들과는 다르게 2가지 기능을 한다.
1. 수학적인 덧셈
2. 문자열 합치기

문자열이 + 연산자의 피연산자로 주어졌을 때, 자바스크립트는 문자열을 숫자로 바꾸려하지 않고 숫자를 문자로 바꾸려한다.

// 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"

객체

자바스크립트에서 객체의 대부분의 암묵적 형변환은 결과 값으로 [object Object]를 반환한다.

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

모든 자바스크립트 객체는 toString메소드를 상속받는다. 상속받은 toString메소드는 객체가 문자열 타입으로 변해야 할 때마다 쓰인다. toString의 반환 값은 문자열 합치기(string concatenation) 혹은 수학적 표현식(mathematical expressions)과 같은 연산에서 쓰이게 된다.

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

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

baz + "!" // "I'm object baz!"

객체가 수학적 표현식 사이에 들어갔을 때는, 자바스크립트는 반환 값을 숫자로 변환하려 할 것이다.

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

배열을 어딘가로 넘길 때는 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 메소드를 정의하는 것도 가능하다.

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

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

객체에 toStringvalueOf 메소드가 전부 정의되어 있을 때는 자바스크립트 엔진은 valueOf 메소드를 사용한다.

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

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

valueOf메소드는 객체가 어떠한 숫자값을 나타낼 때 사용하기 위해 만들어졌다.

Falsy, Truthy

모든 자바스크립트 값은 truefalse로 변환될 수 있는 특성을 갖고 있다. ture로 형변환을 강제하는 것을 truthy라고 한다. 또 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

위 코드처럼 truthy를 이용해도 괜찮다. 하지만 값이 참임을 명시적으로 표현해주는 것이 더 좋은 작성법이다. 명심해야 할 것은 만일 자바스크립트의 묵시적 형변환(implicit coercion)을 완벽히 이해하더라고, 자바스크립트의 묵시적인 형변환에 의존하지 말아햐 한다.

아래 코드 대신에

const counter = 2

if (counter)

다음의 코드가 훨씬 좋은 코드이다.

if (counter === 2)
  
// or

if (typeof counter === "number")

아래 함수는 number 타입의 변수를 받아 실행하도록 하고싶어 만든 함수이다.

const add = (number) => {
	if(!number) new Error("Only accepts arguments of type: number")
	// your code
}

위와 같은 경우에는 인자 값으로 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 tyep: number")
	// tour code
}

add(0) // no error

NaN

NaN은 자기 자신과도 같지 않은 특별한 숫자 값이다.

NaN === NaN // false

const notANumber = 3 * "a" // NaN

notANumber == notANumber // false
notANumber === notANumber // false

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

profile
주니어 프론트엔드 개발자 👼🏻

1개의 댓글

comment-user-thumbnail
2023년 12월 4일

안녕하세요! 자바스크립트의 암묵적 타입변환에 대한 글을 찾다가 이 글을 발견했습니다!
혹시 괜찮으시면 해당 글을 바탕으로 재정리&각색 해서 제 벨로그에 작성해도 될까요? (공부용도입니다)

답글 달기