본문 바로가기
JAVASCRIPT

[JS][강제변환] 암시적 강제변환 : 문자열 <-> 숫자

by KBS 2022. 2. 11.
728x90

암시적 강제변환 : 문자열 <-> 숫자

앞서 살펴보았던 '문자열 <-> 숫자'의 명시적 강베변환을 이제부터 암시적 강제변환 방식으로 살펴보자. 그 전에 암시적 강제변환을 일으키는  연산의 의미를 알아보자.

+연산자는 '숫자의 덧셈, 문자열 접합' 두 가지 목적으로 모버로드 된다. 자바스크립트 엔진은 연산자를 보고 어떤 연산을 해야 할지 어떻게 알까?

var a = "42";
var b = "0";

var c = 42;
var d = 0;

a + b; // "420"
c + d; // 42

 

결괏값이 "420"으로 나올 때와 42로 나올 때는 어떤 차이가 있을까? 피연산자가 한쪽 또는 양쪽 모두 문자열인지 아닌지에 따라 +연산자가 문자열 붙이기를 할지 결정한다고 보통 잘못 알고있는 경우가 많다. 부분적으론 맞지만 실상은 그보다 더 복잡하다.

var a = [1, 2];
var b = [3, 4];

a + b; //"1,23,4"

 

피연산자 a, b모두 문자열이 아니지만 분명히 둘 다 문자열로 강제변환된 후 접합됐다. 무슨일이 일어난 걸까?

ES5 11.6.1에 따르면 +알고리즘은 한쪽 피연산자가 문자열이거나 다음 과정을 통해 문자열 표현형으로 나타앨 수 있다면 문자열 붙이기를 한다. 따라서 피연산자중 하나가 객체(배열 포함)라면, 먼저 이 값에 ToPrimitive 추상연산을 수행하고, 다시 ToPrimitivenumber콘텍스트 힌트를 넘겨 [[DefaultValue]] 알고리즘을 호출한다.

잘 뜯어보면 앞 단락의 작업이 ToNumber 추상연산이 객체를 다루는 방법과 정확히 일치함을 알 수 있다. valueOf()에 배열을 넘기면 단순 원시 값을 반환할 수 없으므로 보통은 toString()으로 넘어간다. 그래서 두 배열은 각각 "1, 2"와 "3, 4"가 되고, +는 두 문자열을 붙여 최종 결괏값은 "1,23,4"이 된다.

간단히 정리해보자. +연산의 한쪽 피연산자가 문자열이면(또는 좀 전과 같이 어떤 과정을 거쳐 문자열이 되면) +는 문자열 붙이기 연산을 한다. 그밖에는 언제나 숫자 덧셈을 한다.

잘 알려진 강제변환 함정이 있다. [] + {} 대 {} + [] 연산 괄과는 각각 "[object Object]"와 0이다.

그런데 이게 암시적 강제변환과 무슨 상관일까?

숫자는 공백 문자열 ""와 더하면 간단히 문자열로 강제변환된다.

var a = 42;
var b = a + "";

b; // "42"

 

a + ""는 숫자를 문자열로 (암시적)강제변환하는 아주 흔한 관용 코드다. 흥미로운 건 그렇게 암시적 강제변환을 맹렬하게 비반하는 이들도 자신의 코드에선 (명시적대신)암시적 강제변환을 즐겨 쓴다는 것이다.

얼마나 욕을 먹던 간데, a + ""같은 코드가 암시적 강제변환의 아주 멋진 사례라고 생각한다.

명시적 강제변환 String(a)에 비해 암시적 강제변환 a + ""에서는 한 가지 유의해야 할 기벽이 있다. ToPrimitive 연산 과정에서 a + ""는 a값을 valueOf() 메서드에 전달하여 호출하고 그 결괏값은 ToString 추상 연산을 하여 최종적인 문자열로 변환된다. 그러나 String(a)toString()를 직접 호출할 뿐이다.

두 방법 모두 궁극적으로 변환된 문자열을 반환하지만 평범한 원시 숫자 값이 아닌 객체라면 결괏값이 달라질 수 있다.

var a = {
  valueOf: function () {
    return 42;
  },
  toString: function () {
    return 4;
  },
};

a + ""; // "42"

String(a); // "4"

 

대부분의 경우 우리가 애써 어지러운 자료구조와 복잡한 로직을 구사하려고 안간힘을 쓰지 않는다면 이런 함정 때문에 밤을 지새울 일은 없겠지만, valueOf(), toString() 메서드를 직접 구현한 객체가 있으면 강제변환 과정에서 결괏값이 달라질 수 있으니 조심해야 한다.

방향을 바꾸어 '문자열 -> 숫자' 암시적인 강제변환을 알아보자.

var a = "3.14";
var b = a - 0;

b; // 3.14

 

-연산자는 숫자 뺄셈 기능이 전부이므로 a - 0은 a값을 숫자로 강제변환한다. 자주 쓰이지는 않지만 a * 1이나 a / 1의 연산자 역시 숫자만 연산하므로 마찬가지다.

객체 값에 -연산을 한다면?

var a = [3];
var b = [1];

a - b; // 2

두 배열은 우선 문자열로 강제변환된 뒤(toString()으로 직렬화) 숫자로 강제변환된다. 그리고 마지막엔 - 연산을 한다.

'문자열 <-> 숫자'의 암시적인 강제변환이 우리가 여태껏 감상한 호러 무비의 주인공만큼 흉측해 보이는가? 그렇지는 않다.

b = String(a)b = a + ""를 비교해보자. 둘 다 경우에 따라 유용하게 코드에 쓰일 수 있찌만 자바스클비트 프로그램에선 후자를 훨씬 더 많이 쓴다. 이것만보더라도 암시적 강제변환이 좋다, 나쁘다 하는 감정과는 무관하게 그 가치를 인정받았다는 것이다.

 


참고

  • You Don't Know JS - 타입과 문법, 스코프와 클로저( 한빛 미디어 )
728x90

댓글