암시적 강제변환 : 문자열 <-> 숫자
앞서 살펴보았던 '문자열 <-> 숫자'의 명시적 강베변환을 이제부터 암시적 강제변환 방식으로 살펴보자. 그 전에 암시적 강제변환을 일으키는 연산의 의미를 알아보자.
+연산자는 '숫자의 덧셈, 문자열 접합' 두 가지 목적으로 모버로드 된다. 자바스크립트 엔진은 연산자를 보고 어떤 연산을 해야 할지 어떻게 알까?
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
추상연산을 수행하고, 다시 ToPrimitive
는 number
콘텍스트 힌트를 넘겨 [[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 - 타입과 문법, 스코프와 클로저( 한빛 미디어 )
'JAVASCRIPT' 카테고리의 다른 글
[JS][강제변환] 암시적 강제변환 : * -> 불리언, || 와 && 연산자 (0) | 2022.02.11 |
---|---|
[JS][강제변환] 암시적 강제변환 : 불리언 -> 숫자 (0) | 2022.02.11 |
[JS][강제변환] 암시적 강제변환 : 개요 (0) | 2022.02.11 |
[JS][강제변환] 명시적 강제변환 : * -> 불리언 (0) | 2022.02.10 |
[JS][강제변환] 명시적 강제변환 : 숫자 형태의 문자열 파싱 (0) | 2022.02.10 |
댓글