2015년 12월 22일 화요일

javascript: Hoisting / 호이스팅

Javascript의 Hoisting에 대해 소개합니다...


Hoisting( [명사] 끌어 올리기; 들어올려 나르기. / 네이버 영어사전).
단어의 뜻만 보자면 선뜻 이해가 안될 수도 있습니다.

가장 쉬운 예로,  함수를 가장 마지막에 선언하고, 그 함수를 가장 첫줄에서 호출해도 별 문제없이 동작하는 것에 그 비밀이 숨어있습니다. 이 호이스팅은 아주 훌륭한 특성입니다. 인터프리터 언어인 자바스크립트의 파싱성능(일반적인 멀티패스 방식의 컴파일링 알고리즘에서 구조/의미분석 단계가 더 빨라질수 있습니다)을 개선하고, 개발자는 별도의 선언없이 필요할때 마다 변수를 선언하여 사용할 수 있습니다.
아주 일반적인 아래의 코드를 보시죠.

console.log( myFunction() );
function myFunction() { return "내 함수";}

위에서 myFunction()이 호출될 당시에 사실 myFunction이란 것은 정의되지 않은 상태로 보입니다. 하지만 아무런 문제없이 Javascript Engine은 뒤에 정의된 myFunction을 정상적으로 호출해 줍니다.
이 현상(?)은 Javascript엔진이 해당 스크립트를 로드할 때 알아서 myFunction의 정의부를 맨 위로 끌어올려 주기 때문에 가능한 일입니다. 예를 들면 아래와 같이 말이죠.

function myFunction() { return "내 함수";}
console.log( myFunction() );

일단 간단히 설명 하자면, 함수 정의부는 모두 위로 끌어올려져서 모두다 미리 파싱이 되어진다는 것입니다. 이 이외에 함수표현식( FE; Function Expression )을 사용하는 경우는 약간 양상이 다른데, 이 이야기는 더 많은 할 이야기가 있으니 다른 포스팅에서 말씀드리도록 하겠습니다.

어찌보면 호이스팅은 자바스크립트 엔진의 어쩔 수 없는 선택인것 같기도 합니다. 하지만 이러한 점 때문에 정말 조심해야 하는 경우도 생깁니다. 다음 코드의 결과를 한번 생각해 보시죠.

var counter = 0;
counter++ ;
function printOut() {
    console.log( counter );
    var counter = 0;
    console.log( counter );
}
printOut();

결과는 아래와 같습니다.
undefined
0

도대체 왜일까요?
전역변수가 1이 되어
1
0
이 나와야할것 같은데 말이죠?

아래는 위의 코드에 자바스크립트 파서가 호이스팅을 적용한 결과입니다.

function printOut() { // function hoisted
    var count; // variable hoisted
    console.log( counter );
    counter = 0;
    console.log( counter );
}
var counter;  //variable hoisted
counter = 0;counter++ ;
printOut();

먼저 함수 정의부를 가장먼저 위로 끌어올리고, 그다음 변수의 선언문을 끌어올립니다.

자바스크립트의 이러한 호이스팅 특성을 모르는 경우라면 이건 버그라고 생각될 수도 있습니다. 그래서 혼란을 겪게 될 수도 있습니다. 로직이 매우 복잡한 경우에는 더욱 그렇구요. 대체로 이 호이스팅은 아주 훌륭하게 동작합니다. 하지만 모든 함수의 정의부가 모두 처음에 로딩되기 때문에 정의된 함수가 Running Scope에 많은 경우라면 성능이 떨어지는 경우도 발생할 수 있습니다.( 이 부분 역시 차후에 함수 표현식에 대해 포스팅할 때 말씀드리겠습니다. )

그래서 일반적으로 버그의 가능성을 줄이고자 모든 선언은 코드블럭의 최상단에 하는것을 추천 합니다. 또는 "use strict"라는 컴파일러 지시자를 통해 이런한 부작용을 인터프리터의 도움을 받아 회피할 수도 있습니다. 이 use strict에 대해선 W3Schools를 참고해 주세요. ("use strict" 는 IE8, 9 등 일부 구식 브라우져에선 지원하지 않으니 이점 참고해 주세요.)

"use strict";
var counter = 0;
counter++ ;
function printOut() {
    console.log( counter ); // Error
    var counter = 0;
    console.log( counter );
}
printOut();

호이스팅을 정리하면,

  1. 함수 정의부 호이스팅
  2. 변수 선언부 호이스팅
위와 같이 크게 두 단계가 가장 먼저 수행됨을 항상 생각해 주세요. 뜻하지 않는 버그를 피하기 위해선 항상 변수를 각 실행 블럭의 가장 최상단에 정의한다고 생각하시면 됩니다.

호이스팅에 대해선 여기까지만 하겠습니다.

댓글 없음:

댓글 쓰기