[딥 다이브 스터디] 23.01.02 - 타입 변환

딥 다이브 스터디 타입 변환 부분입니다.
윤여찬's avatar
Jan 02, 2024
[딥 다이브 스터디] 23.01.02 - 타입 변환
앞서 공부한 자바스크립트는 모든 값이 타입이 존재한다라고 배웠다.
하지만 암묵적으로 자바스크립트 엔진에 의해 자동으로 타입이 변환 된다고 까지 배웠다.
개발자에 의해 의도적으로 값의 타입을 변환하는 것은 명시적 타입 변환 또는 타입 캐스팅이라고한다.
let x = 10; // 명시적 타입 변환 let str = x.toString(); // 숫자를 문자열로 타입 캐스팅한다. log(typeof str); // string
동적 타입 언어인 자바스크립트는 암묵적으로 엔진에 의해 타입이 변환되기도 하는데
이를 암묵적 타입 변환 또는 타입 강제 변환이라고한다.
let x = 10; // 암묵적 타입 변환 // 숫자 타입 x의 값을 바탕으로 새로운 문자열 타입의 값을 생성해 표현식을 평가 let str = x + ''; // '' 문자열을 더함으로 인해 string으로 변환 log(typeof str, str); log(x); // 10
자바스크립트 문법을 잘아는 개발자라고 가정해보자 그럼
x.toString(); // 보다는 x + ''; // 아래가 더 읽기 쉬울 것이다.
위 예제 처럼 문법을 잘 아는 자바스크립트 개발자라고 한다면 아래 + 를 이용한 타입 변환이 가독성이 더 좋을 것이다. 그렇기에 문법을 아는것은 중요하다.

암묵적 타입 변환

자바스크립트 엔진은 표현식을 평가할 때 문맥, 즉 컨텍스트에 고려하여 암묵적 타입 변환을 실행한다. 예제를 보면
// 표현식이 모두 문자열 타입이여야 하는 컨텍스트 '10' + 2 // 102 `1 * 10 = ${ 1 * 10 }` // 1 * 10 = 10 // 표현식이 모두 숫자 타입이여야 하는 컨텍스트 5 * '10' // 50 // 표현식이 불리언 타입이여야 하는 컨텍스트 !0 // true
이처럼 표현식을 평가할 때 문맥, 즉 컨텍스트에 부합하지 않는 다양한 상황이 발생할 수 있다. 이 때 자바스크립트는 가급적 에러를 발생시키지 않도록 암묵적 타입 변환을 통해 표현식을 평가한다.

문자열 타입으로 변환

1 + '2' // 12
자바스크립트 엔진은 문자열 연결 연산자 표현식을 평가하기 위해 문자열 연결 연산자의 피연산자 중에서 문자열 타입이 아닌 피연산자를 문자열 타입으로 암묵적 타입 변환한다.
console.log(`1 + 1 = ${1 + 1}`); // "1 + 1 = 2"
자바스크립트 문자열 암묵적 타입 변환 예제
// 숫자 타입 0 + '' // "0" -0 + '' // "0" 1 + '' // "1" -1 + '' // "-1" NaN + '' // "NaN" Infinity + '' // "Infinity" -Infinity + '' // "-Infinity" // 불리언 타입 true + '' // "true" false + '' // "false" // null 타입 null + '' // "null" // undefined 타입 undefined + '' // "undefined" // 심볼 타입 (Symbol()) + '' // TypeError: Cannot convert a Symbol value to a string // 객체 타입 ({}) + '' // "[object Object]" Math + '' // "[object Math]" [] + '' // "" [10, 20] + '' // "10,20" (function(){}) + '' // "function(){}" Array + '' // "function Array() { [native code] }"

숫자 타입으로 변환

1 - '1' // 0 1 * '10' // 10 1 / 'one' // NaN
위 예제는 모두 산술 연산자이다. 산술 연산자의 역할은 숫자 값을 만드는 것이다.
따라서 산술 연산자의 피연산자는 문맥, 즉 컨텍스트 상 숫자 타입이여야 한다.
 
자바스크립트 엔진은 산술 연산자 표현식을 평가하기 위해 산술 연산자의 피연산자 중에서 숫자 타입이 아닌 피연산자를 숫자 타입으로 암묵적 타입 변환한다.
이때 피연산자를 숫자 타입으로 변환할 수 없는 경우엔 NaN을 반환한다.
'1' > 0 // true
비교 연산자의 역할은 불리언 값을 만드는 것이기에 > 비교 연산자는 blooen 값을 반환한다.
// 문자열 타입 +'' // 0 +'0' // 0 +'1' // 1 +'string' // NaN // 불리언 타입 +true // 1 +false // 0 // null 타입 +null // 0 // undefined 타입 +undefined // NaN // 심볼 타입 +Symbol() // TypeError: Cannot convert a Symbol value to a number // 객체 타입 +{} // NaN +[] // 0 +[10, 20] // NaN +(function(){}) // NaN
+ 단항 연산자는 피연산자가 숫자 타입의 값이 아니면 숫자 타입의 값으로 암묵적 타입 변환을 수행한다.

불리언 타입으로 변환

if ('') console.log(x);
if 문이나 for 문과 같은 제어문의 조건식은 불리언 값, 즉 논리적 참, 거짓을 반환해야 하는 표현식이다. 자바스크립트 엔진은 제어문의 조건식을 평가 결과를 불리언 타입으로 암묵적 타입 변환한다.
if ('') console.log('1'); if (true) console.log('2'); if (0) console.log('3'); if ('str') console.log('4'); if (null) console.log('5'); // 2 4

Truthy 값(참으로 인식할 값) 또는 Falsy 값(거짓으로 인식할 값)

Falsy 값

  • false
  • undefined
  • null
  • 0, -0
  • NaN
  • ‘’ (빈 문자열)

Falsy 값 이외의 값은 제어문의 조건식과 같이 불리언 값으로 평가되어야 할 컨텍스트에서 모두 true로 평가되는 Truthy 값이다.

명시적 타입 변환

이건 사실 개발자가 의도한대로 바꾸는 것이기에 크게 어렵지 않다. 명령어만 잘안다면 적용 가능하다. 아래 예시들을 보자
// 문자열 타입이 아닌 값을 문자열 타입으로 변환하는 방법은 아래와 같다. // 1. String 생성자 함수를 new 연산자 없이 호출하는 방법 // 2. Object.prototype.toString 메소드를 사용하는 방법 // 3. 문자열 연결 연산자를 이용하는 방법 // 1. String 생성자 함수를 new 연산자 없이 호출하는 방법 // 숫자 타입 => 문자열 타입 console.log(String(1)); // "1" console.log(String(NaN)); // "NaN" console.log(String(Infinity)); // "Infinity" // 불리언 타입 => 문자열 타입 console.log(String(true)); // "true" console.log(String(false)); // "false" // 2. Object.prototype.toString 메소드를 사용하는 방법 // 숫자 타입 => 문자열 타입 console.log((1).toString()); // "1" console.log((NaN).toString()); // "NaN" console.log((Infinity).toString()); // "Infinity" // 불리언 타입 => 문자열 타입 console.log((true).toString()); // "true" console.log((false).toString()); // "false" // 3. 문자열 연결 연산자를 이용하는 방법 // 숫자 타입 => 문자열 타입 console.log(1 + ''); // "1" console.log(NaN + ''); // "NaN" console.log(Infinity + ''); // "Infinity" // 불리언 타입 => 문자열 타입 console.log(true + ''); // "true" console.log(false + ''); // "false" // ------------------------------------------------------------------------------ // 숫자 타입이 아닌 값을 숫자 타입으로 변환하는 방법은 아래와 같다. // 1. Number 생성자 함수를 new 연산자 없이 호출하는 방법 // 2. parseInt, parseFloat 함수를 사용하는 방법(문자열만 변환 가능) // 3. 단항 연결 연산자를 이용하는 방법 // 4. 산술 연산자를 이용하는 방법 // 1. Number 생성자 함수를 new 연산자 없이 호출하는 방법 // 문자열 타입 => 숫자 타입 console.log(Number('0')); // 0 console.log(Number('-1')); // -1 console.log(Number('10.53')); // 10.53 // 불리언 타입 => 숫자 타입 console.log(Number(true)); // 1 console.log(Number(false)); // 0 // 2. parseInt, parseFloat 함수를 사용하는 방법(문자열만 변환 가능) // 문자열 타입 => 숫자 타입 console.log(parseInt('0')); // 0 console.log(parseInt('-1')); // -1 console.log(parseFloat('10.53')); // 10.53 // 3. + 단항 연결 연산자를 이용하는 방법 // 문자열 타입 => 숫자 타입 console.log(+'0'); // 0 console.log(+'-1'); // -1 console.log(+'10.53'); // 10.53 // 불리언 타입 => 숫자 타입 console.log(+true); // 1 console.log(+false); // 0 // 4. * 산술 연산자를 이용하는 방법 // 문자열 타입 => 숫자 타입 console.log('0' * 1); // 0 console.log('-1' * 1); // -1 console.log('10.53' * 1); // 10.53 // 불리언 타입 => 숫자 타입 console.log(true * 1); // 1 console.log(false * 1); // 0 // ------------------------------------------------------------------------------ // 불리언 타입이 아닌 값을 불리언 타입으로 변환하는 방법은 아래와 같다. // 1. Boolean 생성자 함수를 new 연산자 없이 호출하는 방법 // 2. ! 부정 논리 연산자를 두번 사용하는 방법 // 1. Boolean 생성자 함수를 new 연산자 없이 호출하는 방법 // 문자열 타입 => 불리언 타입 console.log(Boolean('x')); // true console.log(Boolean('')); // false console.log(Boolean('false')); // true // 숫자 타입 => 불리언 타입 console.log(Boolean(0)); // false console.log(Boolean(1)); // true console.log(Boolean(NaN)); // false console.log(Boolean(Infinity)); // true // null 타입 => 불리언 타입 console.log(Boolean(null)); // false // undefined 타입 => 불리언 타 입 console.log(Boolean(undefined)); // false // 객체 타입 => 불리언 타입 console.log(Boolean({})); // true console.log(Boolean([])); // true // 2. ! 부정 논리 연산자를 두번 사용하는 방법 // 문자열 타입 => 불리언 타입 console.log(!!'x'); // true console.log(!!''); // false console.log(!!'false'); // true // 숫자 타입 => 불리언 타입 console.log(!!0); // false console.log(!!1); // true console.log(!!NaN); // false console.log(!!Infinity); // true // null 타입 => 불리언 타입 console.log(!!null); // false // undefined 타입 => 불리언 타입 console.log(!!undefined); // false // 객체 타입 => 불리언 타입 console.log(!!{}); // true console.log(!![]); // true

단축 평가

'Cat' && 'Dog' // "Dog"
논리곱 연산자 && 두개가 전부 true일 때 true를 반환한다.
대부분의 연산자가 그렇듯이 논리곱 연산자도 오른쪽에서 왼쪽으로 평가가 진행된다.
  1. Cat은 참 같은 값이니 true로 평가된다.
  1. 두번째 Dog도 참 같은 값이기에 true로 평가된다.
  1. 논리곱 연산자 &&는 논리 연산의 결과를 결정한 두번째 피연산자의 평가 결과 즉, 문자열 Dog를 그대로 반환한다.
'Cat' || 'Dog' // "Cat"
논리합 연산자는 || 둘중에 하나만 truetrue를 반환한다.
위와 동일하게 오른쪽에서 왼쪽으로 평가한다.
  1. 첫번째 피연산자 Cat은 참 같은 값이므로 true로 평가된다. 이때 두번째 식은 볼 필요도 없음으로 반환한다.
논리곱 연산자 &&와 논리합 연산자 ||이와 같이 논리 평가를 결정한 피연산자의 평가 결과를 그대로 반환한다. 이를 단축 평가(Short-Circuit evaluation)라 부른다. 단축 평가는 아래의 규칙을 따른다.
true || anything // true false || anything // anything true && anything // anything false && anything // false
단축 평가를 어떻게 잘 쓸 수 있을까? 아래 예시를 보자
let elem = null; console.log(elem.value); // TypeError: Cannot read property 'value' of null console.log(elem && elem.value); // null
객체는 키와 값으로 구성된 프로퍼티들의 집합이다.
위 예제 처럼 객체의 프로퍼티를 참조하면 타입 에러가 발생한다. 이 때 아래 예제 처럼 단축 평가를 사용하면 에러가 발생안한다.
// 단축 평가를 사용한 매개변수의 기본값 설정 function getStringLength(str) { str = str || ''; return str.length; } getStringLength(); // 0 getStringLength('hi'); // 2 // ES6의 매개변수의 기본값 설정 function getStringLength(str = '') { return str.length; } getStringLength(); // 0 getStringLength('hi'); // 2
함수를 호출할 때 인수를 전달하지 않으면 매개변수는 undefined를 갖는다. 이때 단축 평가를 사용하여 매개변수의 기본값을 설정하면 undefined로 인해 발생할 수 있는 에러를 방지할 수 있다.
 
Share article

찬찬잉