본문 바로가기
Language/Javascript

암묵적, 명시적 타입 변환

by joooing 2021. 1. 29.
반응형

Javascript의 모든 값들은 각자 타입을 가지고 있는데, 의도에 따라서 다른 타입으로 변환이 가능하다. 이 때 Javascript 엔진이 코드의 문맥을 고려해서 암묵적으로 데이터 타입을 강제적으로 변환하면 '암묵적 타입 변환(타입 강제 변환)'이라고 한다. 반면 사용자가 의도적으로 타입을 변환하기도 하는데 이를 '명시적 타입 변환(타입 캐스팅)'이라고 한다. 이런 변환이 어떤 식으로 일어나는지 타입 별로 살펴보자.

 

암묵적 타입 변환


Javascript 엔진이 코드의 문맥을 고려해서 암묵적으로 데이터 타입을 강제적으로 변환하는 것이다.

 

1. String 타입으로 암묵적 변환

 

0 + ''  // '0'
-0 + ''  // '0'
-10 + ''  // '-10'
1 + '2'  // '12'
NaN + ''  // 'NaN'

true + ''  // 'true'
null + ''  // 'null'
undefined + ''  // 'undefined'

[] + ''  // ''
[10, 20] + ''  // '10,20'
`1 + 2 = ${1 + 2}`  // '1 + 2 = 3'

 

2. Number 타입으로 암묵적 변환

산술 연산자의 역할은 '숫자 값'을 만드는 것이기 때문에, 엔진도 문맥상 모든 피연산자가 Number타입이여야 한다고 판단해 암묵적으로 타입을 변환한다. 비교 연산자 또한 크기를 비교하는 역할이기 때문에 같은 이유로 Number 타입으로 변환해버린다.

 

10 - '10'    // 0
10 * '10'    // 100
1 / 'one'   // NaN

'1' > 0    // true

 

+ 단항 연산자 또한 피연산자가 Number 타입이 아니면 Number 타입으로 암묵적 타입 변환을 한다. 추가로 특이한 점은 빈문자열, 빈배열은 0으로 변환되지만, 비어있지 않은 배열이나 객체, 함수, undefined는 NaN이 된다는 것이다.

 

+'' // 0
+'0' // 0
+'string' // NaN

+true   // 1
+false  // 0

+null  // 0
+undefined  // NaN

+Symbol()  // TypeError

+{}  // NaN
+[]  // 0
+[10, 20]  // NaN
+(function(){})  // NaN

 

3. boolean 타입으로 암묵적 변환

if문, for문 등의 제어문과 삼항 조건 연산자의 조건식은 boolean값을 평가 결과로 내놓아야 한다. 그래서 Javascript 엔진은 조건식의 결과값을 boolean 타입으로 암묵적 변환한다. 이 때 truthy(참으로 평가되는 값)는 true로, falsy(거짓으로 평가되는 값)는 false로 변환되는데 몇 개의 falsy값을 제외하고는 모두 truthy 이기 때문에 falsy만 기억해두도록 하자!

 

// truthy
if (true) console.log('ok');  // ok
if ('hi') console.log('ok');  // ok

//falsy
if (false) console.log('ok');  // undefined
if (undefined) console.log('ok');  // undefined
if (null) console.log('ok');  // undefined
if (0) console.log('ok');  // undefined
if (-0) console.log('ok');  // undefined
if (NaN) console.log('ok');  // undefined
if ('') console.log('ok');  // undefined

 

 

명시적 타입 변환


명시적 타입 변환은 사용자가 의도적으로 하는 것이기 때문에 비교적 방법이 다양하다. 타입별로 하나씩 살펴보자.

 

1. String 타입으로 명시적 변환

 

// 1. String()
String(10) // '10'
String(NaN)  // 'NaN'

// 2. Object.prototype.toString
(10).toString();  // '10'
(NaN).toString();  // 'NaN'

// 3. 문자열 연결 연산자
10 + '';  // '10'
true + '';  // 'true'

 

2. Number 타입으로 변환

 

// 1. Number()
Number('123');  // 123

// 2. parseInt, parseFloat
parseInt('-1')  // -1
parseFloat('2.15')  // 2.15

// 3. 단항 산술 연산자
+'0';  // 0
+'-1';  // -1
+true;  // 1

// 4. * 산술 연산자
'0' * 1;  // 0
false * 1;  // 0
'-1' * 1  // -1

 

3. boolean 타입으로 변환

 

// 1. Boolean()
Boolean('x');  // true
Boolean('');  // false
Boolean('false')  // true
Boolean(0);  // false
Boolean(1);  // true
Boolean(NaN);  // false
Boolean(Infinity);  // true
Boolean(null);  // false
Boolean(undefined);  // false

// 2. !!
!!'x';  // true
!!'';  // false
!!0;  // false
!!1;  // true
!!NaN; // false
!!Infinity; // true
!!null; // false
!!{};  // true
!![];  // true

 

명시적 타입 변환은 타입을 변경하겠다는 의도가 분명하게 드러나지만, 암묵적 타입 변환은 엔진이 알아서 변환시키기 때문에 의도가 코드에 드러나지 않는다. 가장 중요한 것은 '예측 가능성'을 높임으로써 에러를 방지하는 것인데, 이런 측면에서 보면 명시적 타입 변환을 사용하는 것이 당연해보인다. 하지만 50 + '' 처럼 가끔은 암묵적 타입 변환의 가독성이 더 좋을 때도 있기 때문에 작동 방식을 잘 이해하고 상황에 맞게 사용하는 것이 좋을 것 같다.

반응형

댓글