JavaScript/모던 자바스크립트 DeepDive

9강 타입 변환과 단축 평가

리액트 2023. 4. 27. 10:43

타입 변환과 단축 평가

 

 

9-1 타입 변환이란

  • 타입 변환은 암묵적, 명시적 이렇게 2가지로 나뉨
  • 개발자가 의도적으로 값의 타입을 변환 -> 명시적 타입 변환 (타입 캐스팅)
  • JS엔진에 의해 암묵적으로 변환 -> 암묵적 타입 변환 (타입 강제 변환)
  • 둘다 기존 원시 값을 직접 변경하는 것은 아님! 타입 변환이란 기존 원시 값을 사용해 새로운 원시 값을 생성하는 거임
  • 암묵적 타입 변환은 기존 변수 값을 변경하는게 아니라 새로운 타입의 값을 만들어 단 한 번 사용하고 버림
  • 암묵적이든 명시적이든 예측 가능한 코드를 작성해야 함
  • (10).toString() vs 10+'' 둘 중에 예측 가능한 코드는 뭘까 내가 내 코드를 보든, 동료가 내 코드를 보든 이해가 가능해야한다!
var x = 10;

// 명시적 타입 변환
var str = x.toString();
console.log(typeof str, str) // string 10
console.log(typeof x, x) // number 10 변수의 값이 변경된 것은 아님!!

// 암묵적 타입 변환
var str = x + '';
console.log(typeof str, str) // string 10
console.log(typeof x, x) // number 10 변수의 값이 변경된 것은 아님!!

 


 

9-2 암묵적 타입 변환

  • JS엔진은 코드의 문맥을 고려해 암묵적으로 데이터 타입을 강제 변환함
  • 암묵적 타입 변환이 발생하면 문자열, 숫자, 불리언과 같은 원시 타입 중 하나로 타입을 자동 변환함
// 피연산자가 모두 문자열 타입이어야 하는 문맥
'10' + 2 // 102

// 피연산자가 모두 숫자 타입이어야 하는 문맥
5 * '10' // 50

// 피연산자 또는 표현식이 불리언 타입이어야 하는 문맥
!0 // true
if(1){ } // if문 다음의 블럭 실행 됨

 

9-2-1 문자열 타입으로 변환

// + 연산자는 피연산자 중 하나 이상이 문자열이므로 문자열 연결 연산자로 동작함
1 + '2' // "12"

// ES6에 도입된 템플릿 리터럴의 표현식 삽입은 표현식의 평가 결과를 문자열 타입으로 암묵적 타입 변환함
`1 + 1 = ${1 + 1}` // "1 + 1 = 2"

// 숫자 타입
0 + '' // "0"
-0 + '' // "0"
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]"
[] + '' // ""
[1, 2] + '' // "1, 2"
(function(){}) + '' // "function(){}"
Array + '' // "function Array(){[native code]}"

 

 

9-2-2 숫자 타입으로 변환

  • 산술 연산자의 역할은 숫자 값을 만드는 거임 따라서 코드 문맥상 모두 숫자로 암묵적 타입 변환함
  • 피연산자를 숫자로 못 바꾸는 경우에는 NaN 반환함
  • 비교 연산자의 역할은 표현식의 값을 비교해서 불리언 값을 반환하는 거임 따라서 코드 문맥상 모두 숫자로 암묵적 타입 변환함
  • 빈 문자열(''), 빈 배열([]), null, false 는 0으로 반환 true는 1로 반환
  • 객체와 빈 배열이 아닌 배열, undefined는 변환 안됨 -> NaN 반환
// 산술 연산자
1 - '1' // 0
1 / 'one' // NaN

// 비교 연산자
'1' > 0 // true

// 문자열 타입
+'' // 0
+'0' // 0
+'string' // NaN

// 불리언 타입
+true // 1
+false // 0

// null 타입
+null // 0

// undefined 타입
+undefined // NaN

// 심벌 타입
+Symbol() // TypeError: Cannot convert a Symbol value to a number

// 객체 타입
+{} // NaN
+[] // 0
+[1, 2] // NaN
+(function(){}) // NaN

 

 

9-2-3 불리언 타입으로 변환

  • if문이나 for문 같은 제어문 또는 삼항 연산자의 조건식은 논리적 참/거짓으로 평가되어야 하는 표현식임 JS엔진은 조건식의 평가 결과를 불리언 타입으로 암묵적 타입 변환함
// 2, 4 출력됨
if ('') console.log('1');
if (true) console.log('2');
if (0) console.log('3');
if ('str') console.log('4');
if (null) console.log('5');
  • 이때 JS엔진은 불리언 타입이 아닌 값을 Truthy값 or Falsy값으로 구분함!!!!!
  • 조건식과 같이 불리언 값으로 평가되어야 할 문맥에서 Truthy는 true로 Falsy는 false로 암묵적 타입 변환함
  • falsy로 평가되는 Falsy 값을 익혀두고 잘 활용해보자
// Falsy로 평가되는 친구들
false , undefined, null, 0, -0, NaN, ''(빈 문자열)

// 아래 조건문은 모두 실행됨
if (!false) console.log(false + 'is Falsy value');
if (!undefined) console.log(undefined + 'is Falsy value');
if (!null) console.log(null + 'is Falsy value');
if (!0) console.log(0 + 'is Falsy value');
if (!-0) console.log(-0 + 'is Falsy value');
if (!NaN) console.log(NaN + 'is Falsy value');
if (!'') console.log('' + 'is Falsy value');

 

 

// 전달 받은 인수가 Falsy 값이면 true , Truthy 값이면 false를 반환함
function isFalsy(x){
	return !x;
}

// 전달 받은 인수가 Truthy 값이면 true , Falsy 값이면 false를 반환함
function isTruthy(x){
	return !!x;
}

// 모두 true 반환
isFalsy(false);
isFalsy(undefined);
isFalsy(null);
isFalsy(0);
isFalsy(-0);
isFalsy(NaN);
isFalsy('');


// 모두 true 반환
isTruthy(true);
isTruthy({});
isTruthy([]);
isTruthy('0'); // 빈 문자열이 아닌 문자열은 Truthy 값!!!!!!!!

 

 


 

9-3 명시적 타입 변환

  • 의도를 가지고 타입을 변경하는 방법은 다양함
  • 표준 빌트인 생성자 함수(String, Number, Boolean)를 new연산자 없이 호출하는 방법
  • 빌트인 메서드를 사용하는 방법
  • 암묵적 타입 변환을 이용하는 방법

 

9-3-1 문자열 타입으로 변환

  • String 생성자 함수를 new 연산자 없이 호출하는 방법
  • Object.prototype.toString 메서드를 사용하는 방법
  • 문자열 연결 연산자를 이용하는 방법
// 1. String 생성자 함수를 new 연산자 없이 호출
// 숫자 -> 문자
String(1); // '1'
String(NaN); // 'NaN'
String(Infinity); // 'Infinity'

// 불리언 -> 문자
String(true); // 'true'
String(false); // 'false'


// 2. Object.prototype.toString 사용
// 숫자 -> 문자
(1).toString(); // '1'
(NaN).toString(); // 'NaN'
(Infinity).toString(); // 'Infinity'

// 불리언 -> 문자(true)
(true).toString(); // 'true'
(false).toString(); // 'false'


// 3. 문자열 연결 연산자를 이용하는 방법
// 숫자 -> 문자
1 + ''; // '1'
NaN + ''; // 'NaN'
Infinity + ''; // 'Infinity'

// 불리언 -> 문자(true)
true + ''; // 'true'
false + ''; // 'false'

 

 

9-3-2 숫자열 타입으로 변환

  • Number 생성자 함수를 new 연산자 없이 호출
  • parseInt, parseFloat 함수를 사용 (문자열만 숫자 타입으로 변환 가능)
  • + 단항 산술 연산자 사용
  • * 산술 연산자 사용
// 1. Number 생성자 함수를 new 연산자 없이 호출
// 문자 -> 숫자
Number('1'); // 1
Number('-1'); // -1
Number('1.123'); // 1.123

// 불리언 -> 문자
Number('true'); // 1
Number('false'); // 0


// 2. parseInt, parseFloat 사용
// 문자 -> 숫자
parseInt('1'); // 1
parseInt('-1'); // -1
parseFloat('10.123'); // 10.123


// 3. + 단항 산술 연산자 사용
// 문자 -> 숫자
+'1'; // 1
+'-1'; // -1
+'1.123'; // 1.123

// 불리언 -> 문자(true)
+true; // 1
+false; // 0


// 4. * 산술 연산자 사용
// 문자 -> 숫자
'1' * 1; // 1
'-1' * 1; // -1
'1.123' * 1; // 1.123

// 불리언 -> 문자(true)
true * 1; // 1
false * 1; // 0

 

 

9-3-3 불리언 타입으로 변환

  • Boolean 생성자 함수를 new 연산자 없이 호출
  • ! 부정 논리 연산자를 두번 사용
// 1. Boolean 생성자 함수를 new 연산자 없이 호출
// 문자 -> 불리언
Boolean('x'); // true
Boolean(''); // false
Boolean('false'); // false

// 숫자 -> 불리언
Boolean(0); // false
Boolean(1); // true
Boolean(NaN); // false
Boolean(Infinity); // true

// null -> 불리언
Boolean(null); // false

// undefined -> 불리언
Boolean(undefined); // false

// 객체 -> 불리언
Boolean({}); // true
Boolean([]); // true


// 2. ! 부정 논리 연산자를 두 번 사용
// 문자 -> 불리언
!!'x' // true
!!''; // false
!!'false'; // false

// 숫자 -> 불리언
!!0; // false
!!1; // true
!!NaN; // false
!!Infinity; // true

// null -> 불리언
!!null; // false

// undefined -> 불리언
!!undefined; // false

// 객체 -> 불리언
!!{}; // true
!![]; // true

 

 


 

9-4 단축 평가

 

9-4-1 논리 연산자를 사용한 단축 평가

  • 논리합(||) 논리곱(&&) 연산자 표현식은(불리언이 아닐 수도 있다) 언제나 피연산자 중 어느 한쪽으로 평가된다!!
'cat' && 'dog' // 'dog'
  • && 연산자는 두 개의 피연산자가 모두 true로 평가될 때 true를 반환
  • && 연산자는 왼쪽에서 오른쪽으로 평가가 진행됨
  • 'cat' 은 Truthy 값이므로 true로 평가됨 but 오른쪽 피연산자도 평가해야 전체 표현식을 평가할 수 있음
  • 즉 두 번째 피연산자가 위 &&연산자 표현식의 평가 결과를 결정함!!!!!
  • 이때 논리곱(&&) 연산자는 논리 연산의 결과를 결정하는 두 번째 피연산자 'dog'를 그대로 반환함
'cat' || 'dog' // 'cat'
  • || 연산자는 두 개의 피연산자중 하나만 true로 평가되어도 true를 반환함
  • || 연산자도 왼쪽에서 오른쪽으로 평가가 진행됨
  • 'cat' 이 Truthy 값임 두 번째 피연산자를 평하지 않아도 true로 반환됨
  • 논리 연산의 결과를 결정한 첫 번째 피연산자 'cat'을 그대로 반환함

 

  • && , || 연산자는 논리 연산의 결과를 결정하는 피연산자를 타입 변환하지 않고 그대로 반환 -> 이를 단축 평가라 함
  • 단축 평가는 표현식을 평가하는 도중에 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말함

 

조금 어려운 경우 이런식으로 생각해보자

 

왼쪽이 True 혹은 Truthy값인 경우에!!

- && 은 둘다 true여야 true 인거임 그래서 왼쪽이 true면 오른쪽 까지 true인걸 봐야해서 오른쪽이 평가의 주도권을 가짐

- 그래서 주도권있는 오른쪽 반환

 

 

- || 는 하나만 true여도 true 인거임 그래서 평가가 시작되는 왼쪽이 주도권 있으니까

- 주도권 있는 왼쪽 반환

true || anything // true
false || anything // anything
true && anything // anything
false && anything // false
// || 연산자
'cat' || 'dog' // 'cat'
false || 'dog' // 'dog'
'cat' || false // 'cat'

// && 연산자
'cat' && 'dog' // 'dog'
false && 'dog' // false
'cat' && false // false

표현식의 평가 결과에 대한 주도권 가진 놈이 나온다고 이해 해보셈
&& 은 둘다 true여야 true
|| 는 둘 중에 하나만 true여도 true

 

 

  • 단축 평가를 사용하면 if 문 대체 가능
  • Truthy 값 일때 && 연산자로
  • Falsy 값 일때 || 연산자로
  • 삼항 연산자로 if else 대체
// 1.
var done = true;
var msg = '';

// 주어진 조건이 true일 때
if (done) msg = '완료';

// if문 대체 해보자
msg = done && '완료';
console.log(msg); // 완료


// 2.
var done = false;
var msg = '';

// 주어진 조건이 false일 때
if (!done) msg = '미완';

// if문 대체 해보자
msg = done || '미완';
console.log(msg); // 미완


// 3.
// 삼항 연산자로 if문 대체
var done = 'true';
var msg = '';

// if else
if (done) msg = '완'
else msg = '미완'
console.log(msg); // 완

// 삼항
msg = done ? '완' : '미완';
console.log(msg) // 완

 

 


 

  • 객체와 함수에 대한 내용 나올건데 조금 혼란스러워도 요런 친구가 있다는 것만 알고 넘어가자 (요약 글 쓰는 나도 혼란스럽다;;;)

  • 객체를 가르키기를 기대하는 변수가 null 또는 undefined가 아닌지 확인하고 프로퍼티를 참조할 때 (뭔소리야 이게)
  • 객체는 key value로 구성된 프로퍼티의 집합임
  • 객체를 가르키기를 기대하는 변수의 값이 객체가 아니라 null 또는 undefined인 경우 객체의 프로퍼티를 참조하면 TypeError가 발생

그니까 우리가 객체의 프로퍼티 쓸때 쩜(.) 찍고 꺼내 쓰거나 쩜(.)찍고 값 넣지 않습니까?

그때 꺼내 쓰거나 값을 넣을 때 프로퍼티의 값이 null 이나 undefined라면 TypeError 가 납니다.

그때 타입에러 방지하기 위해서 단축 평가를 쓸 수 있다는 소리 같습니다.. 

아직 저도 많이 부족해서 좀 더 풀어 쓰기가 쉽지 않네요....

 

var x = null;
var value = x.value; // TypeError: Cannot read property 'value' of null

// a가 null 이나 undefined와 같은 Falsy 값이면 a로 평가되고
// a가 Truthy 값이면 a.value2로 평가됨
var a = null;
var value2 = a && a.value2; // null

 

  • 함수 매개변수에 기본값을 설정할 때
  • 함수를 호출할 때 인수를 전달하지 않으면 매개변수에는 undefined가 할당됨
  • 이때 단축평가로 기본값 설정하면 undefined로 인해 발생할 수 있는 에러를 방지 가능!!
// 단축 평가를 사용한 매개변수의 기본값 설정
function getStrLength(str){
	str = str || '';
    return str.length;
}

getStrLength(); // 0
getStrLength("hello"); // 5


// ES6의 매개변수 기본값 설정
function getStrLength(str = ''){
	return str.length;
}

getStrLength(); // 0
getStrLength("niceES6"); // 7

 

 

9-4-2 옵셔널 체이닝 연산자

 

React에서 리덕스 값 받아와서 map filter 돌려서 jsx에 찍어줄때 아주아주아주아주아주 유용함!!!

 

  • ES11에서 도입된 옵셔널 체이닝 연산자 (?. )는 좌항의 피연산자가 null 또는 undefined인 경우 undefined를 반환 아니면 우항의 프로퍼티 참조를 이어감!!!
var a = null;

// a가 null 또는 undefined면 undefined를 반환 아니면 오른쪽 프로퍼티 참조 이어감
var value = a?.value;
console.log(value); // undefined


// --------
// ?. 가 도입되기 이전에는 &&로 변수가 null 또는 undefined인지 확인했음
var b = null;
var value2 =  b && b.value2;
console.log(value2); // null

// --------
// &&는 왼쪽 피연산자가 Falsy값(false, undefined, null, 0, -0, NaN, '')이면 왼쪽 피연산자 그대로 반환
// 왼쪽 피연산자가 0 이나 '' 일때는 객체로 평가될 때도 있음 -> 이건 21강 원시 값과 래퍼 객체에서 볼거임


var str = '';
// 문자열의 길이를 참조
var len = str && str.length;
// 문자열의 길이를 참조 못함
cnosole.log(len); // ''


var str = '';
// 문자열의 길이를 참조 이때 왼쪽 피연산자가 Falsy값이라도 null undefined아니면 오른쪽 프로퍼티 참조 이어감
var len = str?.length;
cnosole.log(len); // 0

 

 

9-4-3 null 병합 연산자

  • ES11에서 도입된 null 병합 연산자 ??는 왼쪽의 피연산자가 null 또는 undefined인 경우 오른쪽 반환
  • null아니면 왼쪽 반환
  • null 병합 연산자 ??는 변수에 기본값 설정할 때 유용 
// 왼쪽 피연산자가 null or undefined면 오른쪽 피연산자 반환
// 아니면 왼쪽 반환
var x = null ?? 'default';
console.log(x); // 'default'


// --------
// ?? 이전에는 || 로 변수에 기본값 설정함 좌항의 피연산자가 Falsy값 (false, undefined, null, 0, -0, NaN, '')
// 이면 우항의 피연산자 반환
// Falsy 값인 0 이나 ''도 기본값으로 유효하다면 예기치 않은 동작 발생할 수 있음
var b = '' || 'default value';
console.log(b); // 'default value'


// --------
// ??는 좌항의 피연산자가 Falsy값이여도 null, undefined아니면 좌항 그대로 반환
var y = '' ?? 'default!!';
console.log(y); // ''

 

 

 


 

굉장히 굉장히 긴 글 읽어주셔서 감사합니다.

중요한 것들이 상당히 많은데 기본적인 타입들의 변환 과정은 항상 예측 가능한 코드를 작성 하는 것

그리고 ?.(옵셔널 체이닝) ??(null 병합 연산자) 의 활용을 중점 적으로 보신다면 조금이나마 도움이 될 것 같습니다.

모두 JavaScript를 위해서 화이팅 합시다.