ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Effective JavaScript [3] - 암묵적 형변환
    Javascript 2016. 11. 29. 21:25



    본 게시물은 Effective Javascript의 내용을 재구성하여 작성되었음을 알립니다. 저작권 문제 발생시 게시물이 비공개 될 수 있습니다. 


    - 데이터형 에러는 암묵적인 강제 형변환에 의해 은밀하게 감춰질 수 있다.

    - + 연산자는 인자의 데이터형에 따라 덧셈이나 문자열 병합으로 오버로딩된다.
    - 객체는 valueOf를 통해 숫자형으로, toString을 통해 문자열로 강제 형변환된다. 
    - valueOf 메서드를 가지는 객체는 반드시 valueOf에 의해 생성되는 숫자 값의 문자열 표현을 생성하는 toString 메서드를 구현해야 한다.

    - undefined 값을 테스트할 때 트루시니스를 사용하기보다는 typeof를 사용하거나 undefined와 비교하는 것이 좋다.



    자바 스크립트 형변환


    자바스크립트는 데이터형 오류에 놀라울 정도로 관대하다. 많은 언어들은 다음과 같은 표현식을 오류로 처리한다. 


    3 + true;    // 4


    true와 같은 불리언 표현식이 산술 연산에 허용되지 않기 때문이다. 자바스크립트에서는 문제없이 실행될 뿐만 아니라 마치 당연하다는 듯이 4라는 값을 반환한다. 



    자바스크립트에서는 잘못된 데이터형으로 인한 오류를 즉시 보여주는 몇가지 경우가 있다. 함수가 아닌데 함수처럼 호출하거나 null 의 프로퍼티에 접근하려고 하는 경우가 바로 그렇다.


    "hello"(1);     // 오류: 함수가 아님

    null.x;    // 오류: null에서 프로퍼티 'x'를 읽어올 수 없음.


    하지만 다른 많은 경우에 자바스크립트는 오류를 발생시키는 대신, 뒤이어 발생하는 다양한 자동 형변환 프로토콜에 따라 예상된 데이터형으로 값을 변환한다. 예를들어 산술 연산자 -, *, /, %는 계산 전에 인자들을 숫자형으로 변환한다. + 연산자는 조금 특이한데, 숫자의 덧셈이나 문자의 병합을 인자들의 데이터형에 따라 오버로딩한다.


    2 + 3; // 5

    "hello" + " world";     // "hello world"


    그렇다면, 숫자와 문자열을 합친다면 어떻게 될까? 자바스크립트는 다음과 같이 문자열을 우선하여 숫자를 문자열로 바꾼다. 


    "2" + 3;    // "23"


    2 + "3";     // "23"


    데이터를 섞어 쓰는 경우 혼란스러울 수 있다. 연산의 순서에 민감하기 때문이다.


    1 + 2 + "3";     // "33"


    (1 + 2) + "3";    // "33"


    1 + "2" + 3;     // "123"


    (1 + "2") + 3;     // "123" 



    비트단위 연산은 숫자로 변환할 뿐만 아니라 32비트 정수로 표현될 수 있는 숫자의 부분집합으로도 변환한다. 이런특징은 비트 단위 산술연산자( ~, &, ^, | )와 시프트 연산자 ( <<, >>, >>>)에 적용된다. 


    형변환은 오류를 숨길 수도 있다. null로 판단된 변수가 산술 연산자에서 오류를 발생하지 않은 채 조용히 0으로 변환되고, 정의되지 않은 변수가 특별한 부동 소수점 값인 NaN (IEEE 부동 소수점 표준에 따른 역설적인 이름인 'not a number')로 변환된다. 이런 형변환은 즉시 예외를 발생시키지 않고, 계산을 계속해서 이뤄지게 하며 혼란스럽고 어려운 값들을 자주 만들어낸다. 


    NaN 값을 테스트 하기는 특히나 어려운데 부동 소수점 표준에 정의된 이상 요구사항을 따라 NaN 자신을 동등하지 않다고 처리하기 때문이다. 따라서 어떤 값이 NaN 인지 테스트하기 위한 다음 식은 전혀 바르게 동작하지 않는다.


    var x = NaN;

    x === NaN;    // false 



    게다가, 표준 isNaN 함수는 스스로 암묵적인 형변환, 즉 값을 테스트하기 전에 인자를 숫자로 바꾸기 때문에 신뢰할 만하지 않다. 이미 값이 숫자인지 알고 있을 경우에는 isNaN으로 테스트 할 수 있다.


    isNaN(NaN);     // true



    하지만 NaN으로 강제 형변환할 수 있는 다른 값들은, 실제로 NaN이 아니라면 isNaN으로 구별할 수 없다.


    isNaN("foo");    // true

    isNaN(undefined);    // true

    isNaN({});    // true


    다행히도 직관적이지는 않지만 NaN을 테스트할 간결하고 신뢰할 만한 코딩 관례가 있다. NaN은 자바스크립트에서 자기 자신과 동일하지 않은 유일한 값이다. 따라서 값이 NaN인지 아닌지는 자기 자신과의 동일함을 확인하여 테스트할 수 있다. 


    var a = NaN;

    a !== a;        // true

    var b = "foo";

    b !== b;     // false

    var c = undefined;

    c !== c;    // false

    var d = { };

    d !== d;    // false

    var e = { valueOf: "foo" };

    e !== e;    // false


    이 패턴을 다음과 같이 명백한 이름의 유틸리티 함수로 추상화할 수도 있다.


    function isReallyNaN(x) {

        return x !== x;

    }


    하지만 자기 자신과 값이 동일하지 않음을 테스트하는 방법은 너무 간단하기 때문에 보통 위의 함수를 만들지 않는다. 



    강제 형변환이 이뤄져 계산이 잘못되었을 때 디버깅을 위한 최선의 방법은 계산의 중간 결과, 즉 문제가 되기 전의 마지막 지점으로 돌아가 조사하는 것이다. 거기서부터 각 연산의 인자들을 조사하고 인자의 데이터형이 잘못되었는지 살펴본다. 버그에 따라서, 잘못된 산술 연산자를 사용하는 등의 논리적인 오류일 수도 있고, 혹은 숫자 대신에 정의되지 않은 값을 전달하는 등의 데이터형 오류일 수도 있다. 



    객체 또한 원시 데이터형(primitive)으로 강제 형변환될 수 있다. 


    "the Math object: " + Math;    // "the Math object: [object Math]"

    "the JSON object: " + JSON;     // "the JSON object: [object JSON]"


    객체는 암묵적으로 toString 메서드가 호출되어 문자열로 변환된다. 다음과 같이 직접 호출해 테스트해 볼 수 있다. 


    Math.toString();    // "[object Math]"

    JSON.toString();     // "[object JSON]"


    유사하게, 객체는 valueOf 메서드를 통해 숫자로 변환될 수도 있다.


    "J" + { toString: function() { return "S"; } };     // "JS"

    2 * { valueOf: function() { return 3; } };    // 6


    + 는 문자 병합과 덧셈에 모두 오버로딩 되어 사용된다. 이러한 점때문에 약간 애매한 점이 있는데 객체가 toString과 valueOf 메서드 둘 다를 포함할 경우에 +가 어떤 메서드를 호출하게 될지 명백하지 않다. 문자열 병합과 덧셈 연산이 데이터형에 따라 선택되는데, 암묵적인 강제 형변환으로는 데이터형이 실제로 주어지지 않기 때문이다. 자바 스크립트는 보이지 않게 valueOf 메서드를 실행한 후 toString을 실행하여 이런 불확실함을 해소한다. 하지만 이 방법은 누군가 일부러 객체로 문자열 병합을 실행한다면, 다음과 같이 예기치 않게 동작할 수도 있다.


    var obj = {

        toString: function() {

            return  "[object MyObject]";

        },

        valueOf: function() {

            return 17;

        }

    };


    "object: " + obj;    // "object: 17"


    valueOf는 Number 객체처럼, 객체가 실제로 숫자로 된 값을 가질 때 사용되는 것이 맞다. 이런 객체에서 toString과 valueOf 메서드는 문자열 표현 또는 동일한 숫자 표현을 일관되게 반환한다. 따라서 객체가 문자열 병합에 쓰이든 덧셈에 쓰이든, 오버로딩된 + 연산자가 항상 일관되게 동작한다. 보통은 숫자로 강제 형변환 하는 것보다 문자열로 강제 형변환하는 것이 훨씬 더 일반적이고 유용하다.


    강제 형변환의 마지막 종류는 종종 투루시니스(thruthiness) 라고 알려져 있다. if, ||, && 와 같은 연산자는 논리적으로 불리언 값과 함께 동작하는데, 실제는 어떤 값도 수용한다. 자바스크립트 값들은 간단한 암묵적인 강제 형변환에 의해 불리언 값으로 해석될 수 있기 때문이다. 대부분의 자바스크립트의 값은 true 로 처리되는데, 다시 말해 암묵적으로 true 로 강제 형변환된다. 이는 문자열이나 숫자 강제 형변환과 다르게 모든 객체에 적용되며 트루시니스는 어떠한 강제 형변환 메서드도 실행하지는 않는다. 정확히 말하자면 false 로 처리되는 값에는 일곱가지가 있다.


    false, 0, -0, "", NaN, null, undefined 이다. 다른 모든 값은 true로 처리된다. 


    function point(x, y) {

        if ( !x ) {

            x = 320;

        }

        if ( !y ){

            y = 240;

        }

        return { x: x, y: y} ;

    }


    이 함수는 0을 포함해서 false로 처리되는 어떤 값도 무시한다.


    point(0, 0);    // {x : 320, y: 240) 



    undefined 를 확인할 수 있는 더 정확한 방법은 typeof를 사용하는 것이다. 


    function point(x, y) {

        if ( typeof x === "undefined" ) {
            x = 320;
        }
        if ( typeof y === "undefined" ){
            y = 240;
        }
        return { x: x, y: y} ;

    }


    위의 코드는 0과  undefiend를 제대로 구분한다. 


    point();     // { x : 320, y: 240}

    point(0, 0);    // { x : 0, y: 0 }


    다른 방법으로  undefined와 비교할 수도 있다.


    if ( x === undefined ) { ... }



    - 데이터형 에러는 암묵적인 강제 형변환에 의해 은밀하게 감춰질 수 있다.

    - + 연산자는 인자의 데이터형에 따라 덧셈이나 문자열 병합으로 오버로딩된다.

    - 객체는 valueOf를 통해 숫자형으로, toString을 통해 문자열로 강제 형변환된다. 

    - valueOf 메서드를 가지는 객체는 반드시 valueOf에 의해 생성되는 숫자 값의 문자열 표현을 생성하는 toString 메서드를 구현해야 한다.

    - undefined 값을 테스트할 때 트루시니스를 사용하기보다는 typeof를 사용하거나 undefined와 비교하는 것이 좋다.






    끄읕


Designed by Tistory.