본문 바로가기

Javascript

[JavaScript] 강제변환 - 암시적 강제변환, Implicit Coercion

반응형

 

이 글의 내용은 자바스크립트의 강제변환의 좋고 나쁨을 충분히 이해하고,

자신의 프로그램에 적절한지 스스로 현명하게 판단할 수 있도록 하기위한 내용을 작성했다.

 

어떤 값을 다른 타입의 값을 바꾸는 과정이 명시적이면 '타입 캐스팅, Type Casting' 이고,

값이 사용되는 규칙에 따라 암시적이면 '강제변환, Coercion' 이라고 한다.

'타입 캐스팅'은 정적 타입 언어에서 컴파일 시점에, '강제 변환'은 동적 타입 언어에서 런타임 시점에 발생한다.

 

본 글은 '강제 변환'을 세부적으로 나눠 '명시적 강제변한'과 '암시적 강제변환'으로 구별할 것이며,

'명시적 강제변환'은 코드만 봐도 의도적으로 탕입변환을 일으킨다는 사실이 명백한 반면,

'암시적 강제변환'은 다른 작업 도중 불분명한 부수 효과로부터 발생하는 타입변환을 뜻함을 참고바란다.

 

var a = 42;
var b = a + ""; // 암시적 강제변환
var c = String(a); // 명시적 강제변환

typeof a; // "number"
typeof b; // "string"
typeof c; // "string"

 

암시적 강제변환,  Implicit Coercion


암시적 강제변환은 부수 효과가 명확하지 않게 숨겨진 형태로 일어나는 타입 변환이다.

그렇다면 암시적 강제변환이 정말 유해하기만 한걸까? 코드를 간결하게 하기 위해 적절한 사용은 유용한 효과를 가져다 주기도 한다.

 

문자열, String()  숫자, Number()

ES5 명세에 의하면, + 알고리즘은 한쪽 피연산자가 문자열이거나 다음 과정을 통해 문자열 표현현으로 나타낼 수 있으면 문자열 붙이기를 한다. 즉 피연산자 중 하나가 객체(배열 포함)라면, 먼저 이 값에 ToPrimitive 추상 연산을 수행하고, 다시 number context 힌트를 넘겨 [[DeaultValue]] 알고리즘을 호출한다. 

 

따라서 + 연산자의 한쪽 피연산자가 문자열이면 +는 문자열 붙이기 연산을 하고 그 밖에는 언제나 숫자 덧셈을 한다.

그러므로 숫자 + "" 으로 암시적 강제변환을 할 수 있다.

단, 암시적 강제변화 과정에서 ToPrimitive 추상 연산을 하여 최종적인 문자열 변환하므로

     평범한 원시숫자 값이 아닌 객체라면 결과값이 달라질 수 있고,

     valueOf() (혹은 toString())를 직접 구현한 객체도 결과값이 달라질 수 있으므로 주의가 필요하다 !! 

 

var a = "59";
var b = "0";

var c = 59;
var d = 0;

var e = [1, 2];
var f = [3, 4];

a + b; // "590"
c + d; // 59
e + f; // "1,23,4"

var g = c + ""; // ⇢ 암시적 강제변환
g; // "59" 

 

불리언, Boolean() ► 숫자, Number()

암시적 강제변환의 효용성은 복잡한 형태의 불리언 로직을 단순한 숫자 덧셈 형태로 단순화할때 빛을 발한다.

 

예시 ) onlyOne() 는 세 인자 중 정확히 하나만 true/truthy인지 아닌지를 확인하는 함수 생성하기.

var a = true;
var b = false;
 
// AS-IS
function onlyOne(a, b, c) {
	return !!((a && !b && !c) || (!a && b && !c) || (!a && !b && c))
}
 
onlyOne(a, b, b); // true
onlyOne(b, a, a); // false
onlyOne(a, b, a); // false

// TO-BE : 암시적 변환 + 명시적 변환
// truthy 체크시 암시적 강제 변환을 하고 최종 반환 값을 포함한 다른 부분은 명시적 강제변환을 사용함
function onlyOne() {
	var sum = 0;
    for (var i=0; i<arguments.length; i++) {
    	if (arguments[i]) {
        	sum += arguments[i];
        }
    }
    
    return sum == 1;
}

onlyOne(b, a); // true
onlyOne(b, a, b, b, b); // true
onlyOne(b, b); // false
onlyOne(b, a, b, b, a); // false

// TO-BE : 명시적 변환
function onlyOne() {
	var sum = 0;
    for (var i=0; i<arguments.length; i++) {
    	sum += Number(!!arguments[i]);
    }
    
    return sum == 1;
}

onlyOne(b, a); // true
onlyOne(b, a, b, b, b); // true
onlyOne(b, b); // false
onlyOne(b, a, b, b, a); // false

 

* ► 불리언, Boolean

암시적 강제변환은 어떤 값이 강제로 바뀌어야 하는 방향으로 사용할 때 발생한다는 점을 기억하자.

다음은 불리언으로의 암시적인 강제변환이 일어나는 표현식을 열거한 내용이다.

 

- if() 문의 조건 표현식

- for( ; ; )에서의 두번째 조건 표현식

- while() 및 do while() 루프의 조건 표현식

- ? : 삼항 연산시 첫번째 조건 표현식

- ||(논리 OR) 및 &&(논리 AND)의 좌측 피연산자

 

이러한 표현식에서 불리언 아닌 값을 사용하게 되면 앞서 말한 ToBoolean 추상 연산 규칙에 따라 불리언 값으로 암시적 강제변환이 된다.

 

 ||(논리 OR) 및 &&(논리 AND)의 좌측 피연산자 

자바스크립트(파이썬, 루비, ..)에서 이 두 연산자(||, &&)는 다른 언어(C, JAVA, ..)와 다르게 true, false로 반환하는 것이 아닌 좌우측의 피 연산자의 값들 중 하나를 선택하게 된다.

 

즉, && 또는 || 연산자의 결과값이 반드시 불리언 타입이 아니며, 항상 두 피연산자 표현식 중 어느 한쪽 값으로 귀결된다.

 

|| 연산자는 그 결과가 true면 첫 번째 피연산자 값을, false면 두 번째 피 연산자 값을 반환한다.

&& 연산자는 그 결과가 true면 두 번째 피연산자 값을, false면 첫 번째 피 연산자 값을 반환한다.

||, && 연산자의 결과값은 언제나 피연산자의 값 중 하나이고, 필요시 강제변환된 평가 결과가 아니다 !!

 

var a = 59;
var b = "xyz";
var c = null;

a || b; // 59
c || b; // "xyz"

a && b; // "xyz"
c && b; // null

 

심벌, Symbol ► 문자열, String 

ES6부터 등장한 심벌(Symbol)은 문자열로 명시적 강제변환은 허용되나, 암시적 강제변환은 금지되며 시도만 해도 에러가 난다.

게다가 심벌 값은 숫자로도 절대 변환되지 않지만, 불리언 값으로는 명시적/암시적 모두 강제변환이 가능하다.

 

var a = Symbol("테스트");
String(a); // "Symbol(테스트)"

var b = Symbol("테스트");
b + ""; // TypeError: Cannot convert a Symbol value to a string

b + 6 ; // TypeError: Cannot convert a Symbol value to a number
Number(b); // TypeError: Cannot convert a Symbol value to a number

b && true; // true
Boolean(b); // true

 

사실, 원래 의도대로인 symbol을 사용한다면 강제변환할 필요는 거의 없을 것이다 :-)

 

 ★ 특이사항 ★ 

[] + {}; // "[object Object]"
{} + []; // 0

 

( 1 ) + 연산자 표현식의 {}를 실제 값(빈 객체)로 해석되어 []는 "" 로 강제변환되고, {}도 문자열 "[object Object]"로 강제변환되었음

( 2 ) {}는 동떨어진 (아무일도 하지 않는) 빈 블록으로 해석되어 블록 끝을 꼭 세미클론으로 끝내야하는 법이 없어 문제가 되지 않음

       따라서, + [] 표현식에서 명시적으로 []를 숫자 0으로 강제 변환되었음

 


[강제변환 - 추상 연산, Abstract operation] chati.tistory.com/152

 

[JavaScript] 강제변환 - 추상 연산, Abstract operation

이 글의 내용은 자바스크립트의 강제변환의 좋고 나쁨을 충분히 이해하고, 자신의 프로그램에 적절한지 스스로 현명하게 판단할 수 있도록 하기위한 내용을 작성했다. 어떤 값을 다른 타입의 ��

chati.tistory.com

 

[강제변환 - 명시적 강제변환, Explicit Coercion] chati.tistory.com/153

 

[JavaScript] 강제변환 - 명시적 강제변환, Explicit Coercion

이 글의 내용은 자바스크립트의 강제변환의 좋고 나쁨을 충분히 이해하고, 자신의 프로그램에 적절한지 스스로 현명하게 판단할 수 있도록 하기위한 내용을 작성했다. 어떤 값을 다른 타입의 ��

chati.tistory.com

 


[참고] You Don't Know JS 타입과 문법, 스코프와 클로저 - 카일 심슨

 

반응형

❥ CHATI Github