자바스크립트 이런 이슈 혹시 아십니까?

<script language="JavaScript">
    myTickets = new Array();
    startPosArr = new Array();
    endPosArr = new Array();
    selItem;
    eee2 = 0;

    function setMyTicketList(){

        alert("^^ : " + eee2);
}

위와 같은 코드에서 eee2를 alert하는 곳에서 다음과 같은 에러가 발생합니다.

Uncaught ReferenceError: eee2 is not defined

그런데 웃기는 것은 eee2의 전역변수 선언 위치를 맨 첫줄에 아래와 같이 선언하면 정상적으로 실행됩니다.

    eee2 = 0;
    myTickets = new Array();
    startPosArr = new Array();
    endPosArr = new Array();
    selItem;

다른 언어에서는 있을 수 없는 현상이라서 좀 황당해서 질문드립니다. 감사합니다.

  • selItem 이 존재하지 않네요? 그러면 오류가 발생할 것이고 그 이하 변수선언도 수행되지 못합니다. 스크립트 언어의 단점입니다. 컴파일 과정이 없으므로 미리 오류를 발견하기 어려운면이 있습니다. 정영훈 2018.11.30 11:35
  • 정영훈님 답변 감사합니다. UnlimitedJava 2018.11.30 13:28

1답변

  • 좋아요

    2

    싫어요
    채택취소하기

    우선 아래 답변은 제가 공부하려고 적는 것이라서 약간 틀린 곳들이 있을 수 있음을 알려드리며... 제가 이해한 것을 설명드리자면, 실행이 안 되는 것은 eee2가 맨 뒤에 있어서가 아니라 selItem보다 뒤에 있기 때문에, 또는 var selItem;이라고 명시적으로 변수 선언을 안 했기 때문에 그런 겁니다. 참조오류(ReferenceError)의 문제입니다.

    ReferenceError 객체는 존재하지 않는 변수를 참조했을 때 발생하는 에러를 나타냅니다. (...) ReferenceError는 선언된 적이 없는 변수를 참조하려고 할 때 발생합니다. 출처: MDN


    JS에서 존재한다는 것은 뭐냐? 선언(declare)되었다는 것입니다. 물론 다른 언어와 비슷하게 일단 선언만 하고 형과 값을 나중에 할당(assign)하는 것이 가능하죠. 그냥 setItem;이라고만 적으신 것은 아마 그렇게 선언만 해두고 싶으셨던 것 같습니다.

    그런데 JS에서 참조한다는 것은 뭐냐? 해당 수준의 범위(scope)까지 그 존재를 땡겨올린(hoist) 다음 그걸 쳐다보고 지명한다는 것입니다. 호이스팅이라고 하는데, JS의 기본 동작은 함수를 먼저 호이스팅하고 그 다음에 변수들을 호이스팅하며, "뭔가 처음 보는 놈이 벌써 뭔가를 할당까지 받았다" 싶으면 변수로 선언된 것으로 보고 올라갈 수 있는 범위까지 땡겨올립니다.

    따라서 다음 코드는 동작하죠.

    x = 5;
    alert(x);
    // 예상하시는 바로 그 동작을 합니다.
    

    하지만 다음 코드는 브라우저 개발자콘솔의 에러 외에 아무 일도 안 일어납니다.

    x;
    alert(x);
    

    왜냐? 할당도 안 받았고 var x; 하는 식으로 변수라는 선언도 못받은 놈을 뭣하러 전역범위까지 끌어올려다 주냐 이겁니다. 그래서 JS는 "야 잠깐만 이 x 뭐야?"라는 의미의 참조오류를 던지고 처리를 중단합니다.


    자 이제 올려주신 코드를 볼까요. 아래 코드는 로딩되자마자 개발자 콘솔에 selItem is not defined라는 참조오류를 찍고, 함수를 실행시키면 추가로 eee2 is not defined라는 참조오류를 또 찍습니다.

    selItem;
    eee2 = 0;
    function setMyTicketList() {
        alert("eee2: " + eee2);
    }
    

    그럴 만도 하죠. setMyTicketList() 함수 호이스팅 됐고, 이제 변수들을 호이스팅하려고 보니까 갑자기 selItem이란 듣보잡이 보여서 전체 호이스팅이 중단된 겁니다. 그래서 뒤에서 대기 타고 있던 애먼 eee2까지 호이스팅을 못 받고 undefined 신세가 됐거든요. 그러니 나중에 setMyTicketList() 함수를 콜해본들 eee2가 뭔지 모르겠다는 에러만 만나게 됩니다.

    반면 아래 코드는 selItem 레퍼런스 에러까지는 내지만 함수 실행 자체는 시켜줍니다.

    eee2 = 0;
    selItem;
    function setMyTicketList() {
        alert("eee2: " + eee2);
    }
    

    왜 되는지 이해되시겠죠? 매우 우연하게, 다행히, eee2는 존재되어 참조된 겁니다.

    보너스: 다음도 동작합니다. 아래 코드에서 총 3개의 객체는 모두 정상적으로 호이스팅 되었기 때문이죠. (아래 코드의 eee2처럼 어떤 변수를 var, let 같은거 없이 그냥 바로 할당만 하는 방식의 호이스팅은 안 좋긴 한데 그건 좀 다른 얘기니까 생략...)

    var selItem;
    eee2 = 0;
    function setMyTicketList() {
        alert("eee2: " + eee2);
    }
    

    예전에 취직면접 보고 다닐 때 호이스팅 아냐고 물어보는 걸 답 못 해서 개쪽당하고 나온 기억이 나서 공연히 자세히 적어봤습니다. 자세한 건 데모와 함께 좀더 탐구해 보기로 하시죠. 도움이 되었을까요?

    • 엽토군님, 자세한 설명 대단히 감사합니다. 많은 도움이 됐습니다. UnlimitedJava 2018.11.30 13:28

ᕕ( ᐛ )ᕗ
로그인이 필요합니다

작성한 답변에 다른 개발자들이 댓글을 작성하거나 댓글에 좋아요/싫어요를 할 수 있기 때문에 계정을 필요로 합니다.