ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Effective JavaScript [6] - 세미콜론 삽입의 한계에 대해서 알아두자
    Javascript 2016. 11. 30. 21:58



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


    - 세미콜론은 } 앞이나 줄의 마지막 또는 프로그램의 마지막 전에만 추론되어 삽입된다.

    - 세미콜론은 다음 토큰이 파싱될 수 없을 때에만 추론되어 삽입된다.
    - 선언문이 (, [, +, -, /로 시작할 때는 절대 세미콜론을 생략하면 안된다.
    - 스크립트를 병행할 때, 스크립트들 사이에 명시적으로 세미콜론을 삽입하라.
    - return, throw, break, continue, ++, -- 바로 뒤에 새로운 행을 입력하지 마라.

    - for 반복문의 머리 부분(조건부)에서는 세미콜론이 구분자 또는 빈 선언문으로도 절대 추론되어 삽입되지 않는다. 




    자바스크립트의 편리함 중 하나는 문장을 종료하는 세미콜론을 생략할 수 있다는 점이다. 


    function Point(x, y) { 

        this.x = x || 0

        this.y = y || 0

    }



    Point.prototype.isOrigin = function() {

        return this.x === 0 && this.y === 0

    }



    자동으로 삽입되는 세미콜론 덕에 이렇게 동작할 수 있다. 자동 세미콜론 삽입은 특정 문맥에서 생략된 세미콜론을 추론하여 프로그램을 파싱하는 기술로, 프로그램에 세미콜론을 자동으로 삽입해 준다. ECMAScript 표준은 세미콜론 삽입 매커니즘을 정확하게 기술하기 때문에, 선택적인 세미콜론은 자바스크립트 엔진들 사이에 상호호환된다. 



    세미콜론은 한 줄 이상의 새로운 행이나, 프로그램 입력의 마지막이나 } 토큰전에만 삽입된다

    다르게 말하면, 세미콜론은 줄의 마지막 부분, 블록의 마지막 부분, 또는 프로그램의 마지막 부분에서만 생략 가능하다. 따라서 다음은 아무런 문제없는 함수다. 



    function square(x) {

        var n = +x

        return n* n

    }


    function area(r) { r = +r; return Math.PI * r * r }

    function add1(x) { return x + 1}


    하지만 다음은 잘못된 예다.


    function area(r) { r = +r return Math.PI * r * r } // 오류



    두 번째 규칙은 다음과 같다


    세미콜론은 다음 입력 토큰을 파싱할 수 없을 때에만 삽입된다. 


    다시 말해, 세미콜론 삽입은 오류 보정 매커니즘이다. 간단한 예제 코드를 살펴보자.


    a = b

    ( f ( ) );


    다음과 같이 하나의 선언으로도 문제없이 실행될 수 있다.


    a = b ( f ( ) ) ;


    즉, 세미콜론은 삽입되지 않는다. 반대로 다음 코드 조각을 살펴보자. 


    a = b

    f ( );


    이 예제는 두 개의 구분된 선언으로 파싱된다. 왜냐하면 다음과 같이 파싱하는 것은 오류이기 때문이다.


    a = b f ( ) ; 


    다음 줄의 초기 토큰이 이전 선언의 연장선으로 해석될 수 있다면 세미콜론을 생략해선 안된다. 정확히 다슷 개의 문자 (, [, +, -, / 를 조심해야 한다. 문맥에 따라 표현식 연산자로 동작하거나 선언의 접두어로 사용될 수 있기 때문이다. 표현식으로 끝나는 선언을 조심해야 한다.

    가장 일반적인 시나리오는 이전 예제와 같이 괄호로 시작되는 선언이다. 또 다른 일반적인 시나리오로 배열 리터럴이 있다.


    a = b

    ["r", "g", "b"].forEach(function(key) {

        background[key] = foreground[key] / 2;

    } );


    이 예제는 할당문과 선언문으로 구성된 두 개의 선언으로 보이지만 [ 로 시작하는 두 번째 선언 때문에 다음과 같이 하나의 선언으로 파싱된다.


    a = b["r", "g", "b"].forEach(function(key) {

        background[key] = foreground[key] / 2;

    } );



    대괄호 표현식이 이상하게 보일 수 있지만, 자바 스크립트는 쉼표로 구분된 표현식을 허용한다는 사실을 기억해야 한다. 이 표현식은 왼쪽부터 오른쪽으로 평가되고 그 마지막 하위 표현식을 반환한다. 이 경우에는 문자열 "b"를 반환한다. 


    + 와 -, / 토큰은 비교적 드물게 선언문 처음에 나타나는데, 그렇다고 전혀 사용되지 않는 것은 아니다 / 의 경우는 특히 이상한데, 선언의 시작 부분에서 실제로는 전체 토큰이 아니라 정규 표현식 토큰의 시작으로 사용된다. 


    /Error/i.test(str) && fail();


    이 선언은 대소문자를 구분하지 않고 문자열을 테스트하는 정규 표현식이다. 일치되는 문자를 발견하면 fail 함수를 호출한다. 


    일치되는 문자를 발견하면 fail 함수를 호출한다. 하지만 이 코드가 다음과 같이 종료되지 않은 할당문에 뒤이어 나올 수 있다.


    a = b

    /Error/i.test(str) && fail();


    이때 코드는 다음과 같은 선언으로 파싱된다. 


    a = b / Error / i.test(str) && fail();


    다시 말해, 앞부분의 / 토큰이 나눗셈 연산자로 파싱된다. 


    경험이 많은 자바스크립트 프로그래머는 세미콜론을 생략하기 전에, 선언문이 잘못 파싱되지 않기 위해 선언문 다음의 줄을 살펴본다. 또한 리팩터링 할 때도 신경을 쓴다. 예를 들어, 세 개의 추론된 세미콜론을 포함하는 완벽히 정확한 프로그램은 다음과 같다.


    a = b 

    var x

    ( f ( ) )


    코드가 잘못 리팩토링되면 두 개의 추론된 세미콜론만 가지는 다른 프로그램으로 바뀔 수도 있다.


    var x

    a = b // 세미콜론이 삽입되지 않음.

    ( f ( ) )



    var 선언을 한 줄 올렸을 뿐이지만, 이전 예제와 다르게 b 뒤에 괄호가 뒤따라 오기 때문에 프로그램은 다음과 같이 잘못 파싱된다.


    var x;

    a = b( f ( ) );


    결과적으로 생략된 세미콜론에 대해서 인지해야 하고 세미콜론 삽입을 비활성화시키는 토큰이 있는지 다음 줄의 처음 부분을 확인해야 한다. 대체 방법으로 ( 또는 [, +, -, / 로 시작되는 선언문 앞에 추가적인 세미콜론을 접두어로 추가하는 규칙을 따를 수도 있다.


    var x

    a = b

    ; ( f ( ) )




    반드시 선언문에서 세미콜론을 생략하려면 현지 파일의 다음 토큰뿐만 아니라 스크립트 병합으로 인해 따라올 수 있는 다른 토큰도 함께 고려해야 한다.  최소한 첫 선언이 다섯 개의 취약한 문자들 ( , [, +, -, / 중 하나라면 모든 파일에 방어적인 세미콜론을 접두어로 사용하여, 스크립트 병합으로부터 보호할 수 있다.


    // file1.js


    ; ( function ( ) {


    } ) ( )


    이 방법은 이전의 파일이 마지막 세미콜론을 빠뜨렸다고 할지라도 병합된 결과가 구분된 선언으로 처리될 수 있도록 보장한다. 


    스크립트 병합 프로세스가 파일 사이에 세미콜론을 자동으로 삽입하면 좋을 것이지만 모든 병합 도구들이 잘 작성된 것은 아니기 때문에 방어적으로 세미콜론을 추가하는 것이 가장 안전한 방법이다. 



    이쯤에서 이런 생각이 들 수도 있다. "난 절대 세미콜론을 빠뜨리지 않으니 괜찮을거야" 하지만 그렇지 않다. 자바스크립트는 파싱 오류로 판명되지 않더라도 강제적으로 세미콜론을 삽입하는 경우가 있다. 이것들은 소위 자바스크립트 문법의 제한된 생성(restricted production) 이라고 부르는데, 두 토큰 사이에 새로운 행이 허용되지 않는다는 의미다. 가장 치명적인 경우는 return 선언문인데 return 키워드와 그 자신의 부가적인 인자사이에 새로운 행이 포함되지 않아야 한다.


    return { } ;


    이 코드는 새로운 객체를 리턴하는 반면, 다음 코드는 다르게 파싱된다.


    return 

    { } ;


    이는 다음과 같이 세 개의 구분된 선언으로 파싱된다.


    return;

    { }


    다시 말해, return 키워드 다음에 오는 새로운 행은 자동 세미콜론 삽입을 강제한다. 그래서 빈 블록이나 빈 선언이 뒤이어 오는 것처럼 인자가 없는 return 으로 파싱된다. 다음과 같은 경우에도 동일한 세미콜론 규칙이 적용된다.


    - throw 선언문

    - 명시적인 이름표가 있는 break 나 continue 선언문

    - ++나 -- 연산자 접미어


    a

    ++

    b


    ++ 연산자는 접두어 또는 접미어가 될수 있지만, 새로운 행에 뒤이어 접미어로 위치할 수 없다. 따라서 이 코드는 다음과 같이 파싱된다.


    a; ++b; 




    세미콜론 삽입의 세번째 규칙은 다음과 같다. 


    세미콜론은 for 반복문의 구분자나 빈 선언문으로 절대 삽입되지 않는다.


    이 규칙은 간단하게 말해서 for 루프의 머리 부분에 반드시 명시적으로 세미콜론을 포함해야 한다는 뜻이다. 그렇지 않은 코드는 파싱 오류가 발생한다.


    for ( var i = 0, total = 1 //파싱 오류

        i < n

        i ++ ) {

        total * = i

    }


    본문이 비어 있는 루프도 명시적인 세미콜론이 필요하다. 그렇지 않으면 파싱 오류가 난다.


    function infiniteLoop() { while ( true ) } // 파싱 오류


    따라서 이 경우에도 세미콜론이 필요하다


    function infiniteLoop() { while ( true ) ; } 



    요점정리.


    - 세미콜론은 } 앞이나 줄의 마지막 또는 프로그램의 마지막 전에만 추론되어 삽입된다.

    - 세미콜론은 다음 토큰이 파싱될 수 없을 때에만 추론되어 삽입된다.

    - 선언문이 (, [, +, -, /로 시작할 때는 절대 세미콜론을 생략하면 안된다.

    - 스크립트를 병행할 때, 스크립트들 사이에 명시적으로 세미콜론을 삽입하라.

    - return, throw, break, continue, ++, -- 바로 뒤에 새로운 행을 입력하지 마라.

    - for 반복문의 머리 부분(조건부)에서는 세미콜론이 구분자 또는 빈 선언문으로도 절대 추론되어 삽입되지 않는다. 




    끄읕.


Designed by Tistory.