loading
반응형

자바스크립트(javascript) - 타입(유형), 형, 변환 개념잡기

 

순번 링크페이지 아래 제목을 클릭하면 해당 링크페이지로 이동 됩니다.
1 JS - 형 변환에 대하여 - (1)
2 JS - 형 변환에 대하여 - (2)
3 JS - 형 변환에 대하여 - (3)
4 JS - 형 변환에 대하여 - (4)
5 JS - 형 변환에 대하여 - (5)
6 JS - 형 변환에 대하여 - (6)

 

 

▶ 암묵적 형 변환

이 전 글에서 명시적으로 형 변환에 대해 얘기를 했다.

이번엔 명시적 형 변환에 반대인 암묵적 형 변환에 대해 정리해본다.

 

우선 정리하기 앞서 이 암묵적 형 변환이 간단하게 어떤 의미인지 그리고 이 암묵적 형 변환에 있어서 부정적인 시선지양적인 목표로 삼아야 한다고 한다. 

 

이 암묵적이란 의미는 겉으로 드러나지는 않지만 무엇인가 숨겨진 형태라는 의미인데, 

이게 숨겨진 형태로 형 변환이 일어난다?라는 것인데...

 

개발자가 만약 이러한 상황을 겪게 되면 실수도 많아질뿐더러 많은 혼란을 야기시킬 수가 있다.

이 암묵적 강제 형 변환이 일어나면 개발자가 알지 못하는 명확하지 않은 모든 유형 변환이 여기에 속한다.

 

개발자는 코드를 명시적이고 더 이해하기 쉽게 그리고 직관적이게 코딩을 해야,

유지보수라던지 다른 제 3자가 코드를 수정할 시 오류를 범하는 걸 줄일 수 있고 개발자가 지향해야 하는 목표이다.

 

이 암묵적인 형태의 강제 형 변환이 코드를 이해하는데 어렵고, 혼란을 야기시키면서왜?

오류를 쉽게 일으킬 수 있는지 암묵적 형 변환에 대해 알아보자.

 

💻 문자열(String) ↔ 숫자(Number) 

var arr_1 = [1,2];
var arr_2 = [3,4];

console.log(arr_1 + arr_2); // ???

이 console.log의 결과는 어떨까? 이 결과를 물어보기 전 난 이런 질문을 하고 싶다.

과연 이해하기 쉬운가?  

 

 console.log(arr_1 + arr_2);  결과는 "1,23,4"가 나온다.

매우 혼란스럽다. 저 배열 객체 두 개가 더해진 값이 "1,23,4"가 나오다니 말이다.

 

전혀 생각치도 못한 결과가 나와버렸다.

도대체 저 두개의 배열 객체가 더해지면서 무슨 일이 벌어졌길래 저런 값이 나왔을까?

 

우선 어떻게 저런 연산이 되었는지 ES5의 spec을 알아봐야 한다.

ES5 spec 섹션 11.6.1을 봐보자.

 

 ES5 spec #11. 6.1 더하기 연산자 링크

ECMAScript5 spec #11.6.1 The Addition operator ( + )

위 내용을 보면 더하기 연산자는 피연산자 중 하나가 문자열이거나 할 때,

ToString 규칙에 의해 문자열로 바꾼 후 결과를 문자열로 합친 결과로 반환한다.

 

따라서, + 연산자는 피연산자 중 하나에 대해 객체(배열 포함)를 읽어 들여 연산을 막 시작하려고 할 때

먼저 값에 대한 ToPrimitive 추상연산을 실행한 다음 [[DefaultValue]] 알고리즘을 실행한다.

 

ToPrimitive 규칙 ES5 spec # 9.1 링크

[[DefaultValue]] 규칙 ES5 spec # 8.12.8 링크

 

위 예제코드 배열 + 배열의 연산을 하기 위해서 두 개의 규칙이 적용이 되는데,

이 두개의 규칙을 쉽게 설명하자면 현재 두개의 객체를 +(더하기) 연산자를 실행을 했고,

 

자바스크립트는 이 두 개의 배열을 문자열, 숫자가 아닌 객체라는 것을 인지를 했다.

그리고 두개의 + 연산을 하기 위해 암묵적 강제적인 추상 연산 ToPrimitive라는 연산을 했고,

 

이 ToPrimitive 의 연산의 어떤 내용이 있는지 먼저 확인해보자.

ECMAScript5 spec # 9.1 ToPrimitive

위 규칙 내용을 살펴보면 빨간 박스로 되어 있는 부분의 내용은

타입이 객체에 대해 추상연산은 [[DefaultValue]] 내부 메서드를 호출을 한다고 했고,

 

[[DefaultValue]] 내부 메서드는 규칙 8.12.8 에 의해 정의된다고 했다.

ECMAScript5 spec # 8.12.8 [[DefaultValue]]

이 규칙에 의해 객체와 객체 간 (+) 연산 작업이 어떻게 진행되는지 알 수 있다.

이 규칙을 토대로 배열에 대한 (+) 연산 작업을 시도할 때, 배열은 valueOf() 작업을 시도하는데

 

이 작업은 단순 기본형이므로 toString() 메서드의 행위와 같아서

arr_1 배열 [1,2]arr_2 배열 [3,4]는강제 형변환에 의해 문자열 "1,2" 그리고 "3,4"가 된다.

 

여기서 질문? 왜 배열은 valueOf() 왜 시도하는지?
이 부분은 이전 글에서 객체 형 변환에 대해 설명을 해놨다.
링크 글 참조.

링크

 

그래서 두개의 문자열을 (+) 더하는 연산이 이루어져 결과가 "1,23,4"가 나온 것이다.

단순하게 (+) 연산자에 설명을 하자면

 

+ 에 대한 피연산자 중 하나가 문자열이면 결과는 무조건 문자열이 결합된(더해진) 결과가 나오고,

그렇지 않은 숫자이면 덧셈이 된다.

 

var num_1 = "100";
var num_2 = 100;

console.log(num_1 + num_2); // "100100"

 

이 코드의 결과를 보면 암묵적인 형 변환이 이루어져 console.log의 출력 값이 "100100" 이란 결과가 나왔다.

num_2 변수의 숫자 100은 문자열로 바뀐 뒤 (+) 연산이 되어서 "100100" 이란 결과가 나온 것이다.

 

사실 위와 같은 연산은 좋지가 못하다.

만약 저 두개의 변수를 명시적으로 표현하면 어떻겠는가?

var num_1 = "100";
var num_2 = 100;

// 문자열로 결과를 얻고 싶을 경우 - 명시적 형 변환
var num_3 = num_1 + String(num_2);

// 숫자로 결과를 얻고 싶을 경우 - 명시적 형 변환
var num_4 = Number(num_1) + num_2;


console.log(num_3); // "100100"
console.log(num_4); // 200

 

보이는가? 

코드만 그냥 보아도 결괏값을 그냥 머릿속에 도출할 수가 있다.

이렇게 명시적 형 변환을 시도한 후에 연산 작업을 하면, 결과가 어떻게 나올지 눈에 보이고

 

제 삼자가 이런 코드를 보고 수정을 해야 할 경우에도 혼란을 덜 겪는다.

이 처럼 암묵적 형 변환이 코드가 길고 내용이 많아질수록 오류를 일으킬 확률이 커지기 때문에

 

대체로 암묵적 형 변환을 자제하며 지양해야 하고,

명시적 형 변환을 사용을 지향해야 한다.

 

이에 반대 되는 (-) 연산자 경우는 어떤지 봐보자.

var num_1 = "100";
var num_2 = num_1 - 10;

console.log(num_2); // 90

 

결과는 90 이라는 숫자가 나와버렸다. 반대의 결과로 나와버렸다.

즉, 문자열 "100" 이 숫자로 암묵적인 강제 숫자 형 변환이 되어서 숫자 -10 의 연산을 한 결과가 나와버렸다.

이유는? 

 

ECMAScript5 spec # 11.6.2 The Subtraction Operator ( - ) 링크

( - ) 연산자 규칙을 함 봐보자.

 

ECMAScript5 spec # 11.6.2 The Subtraction Operator ( - )

 

규칙 내용을 봐보면 이 ( - ) 연산자는 숫자 빼기에 대해서만 결과를 도출하도록 정의 되어있다.

그래서 문자열 "100" 에서 -10 을 빼니 결과가 90 이라는 숫자로 나왔던 것이다.

 

그렇다면 이러한 경우는 어떻게 될까?

var arr_1 = [100];
var arr_2 = [50];

console.log(arr_1 - arr_2);// ???

 

이것도 마찬가지로 추상작업ToPrimitive 를 수행해 이 규칙이 다시 [[DefaulstValue]] 내부 메서드를 사용을해서

이 배열이 toString() 을 해가지고 문자열을 만들어서 (-) 연산작업을 했다.

 

(-) 연산작업은 숫자로 모두 강제 형변환 후 수행한다고 했으니

arr_1 배열의 100 은 문자열 "100" 으로

arr_2 배열의 50 은 문자열 "50" 으로

 

"100" - "50" 의 결과는 강제 숫자로 형변환 후 결과 값이 50 으로 나온다.

이제 여기서 그렇다면?

 

var arr_1 = [100, 100];
var arr_2 = [50, 50];

console.log(arr_1 - arr_2); // ???

어랏? 이 경우는 그럼 어떻게 되는거지?

음.. 결론은 안 된다. NaN 값이 나온다. 너무 깊게 생각하지말자...

 

이게 만약 내가 알지 못하는 또 다른 값이 나왔다면.. 정말... 자바스크립트 포기해야 하나 싶다...

이렇게 하지도 말아야 하거니와, 개발하면서도 이렇게 써본적도 없지만

 

암묵적 형변환은 진짜 지양해야 하는게 다시 한 번 느낀다.

 

 

반응형

 

💻 Boolean 형 변환 

암묵적 형변환 중 가장 혼란 스러운 boolean 형 변환인데 이 부분도 정리를 해보자.

 

|| 그리고 && (논리연산자)

이 논리 연산자는 자바스크립트 뿐만 아니라 다른 프로그래밍 언어에서도 사용도하고, 

우리가 기본적으로 알고 있는 논리 연산자이다.

 

하지만 자바스크립트에선 이 논리연산자는 다른 언어(C, PHP) 와 좀 다르다.

ECMAScript5 spec # 11.11 Binary Logical Operators 링크

 

ES5 사양을 보면

ECMAScript5 spec #11.11 Binary Logical Operators

 

&& 또는 || 연산자에 의해 생성된 값은 Boolean 유형 일 필요가 없다.

생성된 값은 항상 두 피연산자 표현식 중 하나의 값이다.

 

정의된 바와 같이 논리 연산자 중 참이든 거짓이든 피 연산자의 값이 생성이 된다고 한다.

이 의미가 무엇인지 확인해보자.

 

var num = 100;
var str = "A";
var NULL = null;

console.log(num || str); // 100
console.log(num && str); // "A"

console.log(NULL || str); // "A"
console.log(NULL && str); // null

 

우선 이 예제를 확인해보면 논리 연산자를 작업을 할 때,

ES5 규칙 #9.2 ToBoolean 규칙사용해서 강제 Boolean 형 변환을 시도해서 논리 연산자를 수행한다.

 

ToBoolean 규칙을 사용해서 Boolean 형 변환은 이전 글에서 설명함 ☞ 링크 참조

 

첫번 째 console.log(num || str)

num || str 이 연산을 할 때, 첫번째 피연산자 num 변수가 ToBoolean 규칙에 의해 강제 형 변환이 이루어져,

true 이기 때문에 (num || str) 이 연산 자체의 결과 값이 100 이 된다.

 

즉, 여기서 하고 싶은말은 (num || str) 이 연산 자체의 결과 값이

C언어나 PHP 언어처럼 true 가 아니란 뜻이다.

첫 번째 피 연산자가 true 가 되면 그 피연산자의 변수 값을 반환한다.

 

두번 째 console.log(num && str)

num && str 이 연산을 할 때, 첫번째 피연산자 num 변수가 ToBoolean 규칙에 의해 강제 형 변환이 이루어져,

true 이고, 두번째 피연산자 str 변수도 마찬가지로 ToBoolean 규칙에 의해 강제로 형 변환이 이루어져,

true 이고,

 

그래서 num 변수와 str 변수는 둘다 true 이기 때문에 논리연산자 && 이 것의 자체 결과 값이 str 이 된다.

즉, 여기서 하고 싶은 말은 && 이 논리연산자의 값이 true 이면 피연산자 두번 째 값이 반환이 된다는 것이다.

 

 

세번 째 console.log(NULL || str)

NULL || str 이 연산을 할 때, 첫 번째 피여산자 NULL 변수는 ToBoolean 규칙에 의해 강제 형 변환이 이루어져,

false가 되고, (NULL || str) 이 연산 자체의 결과 값은 "A" 가 된다.

 

즉, 여기서 하고 싶은 말은 첫 피연산자가 NULL 변수는 null 이라는 값을 가지고 있으므로 false 이니깐,

논리연산자 || 는  str 변수 문자열 "A" 라는 값을 가지고 있는 true 값을 선택했고, 

 

그래서 str 변수를 반환을 했다. 그래서 출력 결과가 "A" 가 나왔다.

 

 

네번 째 console.log(NULL && str)

NULL && str 이 연산을 할 때, 첫 번째 피연산자 NULL 변수는 강제 형 변환으로 false 이고, 

두번 째 피연산자 str 변수는 true 이다.

 

논리연산자 &&는 거짓이라는 판별과 함께 NULL 이라는 변수를 반환한다.

 

위 네가지를 다시 요약해서 얘기하면 A 와 B가 있다고 가정하고,

A 값이 true 일 경우, B값이 true 일 경우

  • (A || B) 는 A 값을 반환한다.
  • (A && B) 는 B 값을 반환한다.

 

A값이 false 일 경우, B 값이 true 일경우

  • (A || B) 는 B 값을 반환한다.
  • (A && B) 는 A 값을 반환한다.

 

이러한 Boolean 형변환의 논리연산자로 인해 우리는 자주 이런 문구를, 개발의 코드를 보았을 것이다.

function thingFunction(a, b){
  a = a || '나는',
  b = b || ' JS 공부를 열심히 한다.'
  
  return console.log(a + b);
  
}

thingFunction(); // "나는 JS 공부를 열심히 한다."
thingFunction(null, ' 버스를 타고 집에 간다.'); // "나는 버스틀 타고 집에 간다."

 

이렇게 형변환을 이용한 함수 내에 변수를 초기화 하는 형식으로 많이 사용을 한다.

 

 

 

반응형

+ Recent posts