ECMAScript(Javascript) 호이스팅

호이스팅(Hosting)은 자바스크립트 인터프리터가 변수 및 함수의 선언을 항상 코드 상단으로 올리는 행위를 말한다. 엄밀하게 얘기하자면 컴파일 단계에서 변수 및 함수의 선언을 먼저 읽어서 메모리 상에 저장해두는 행위에 가깝다. 예제 코드를 한 번 보자.

1
2
3
a = 1;
console.log('Result:', a);
var a;

이 코드의 실행 결과는 아래와 같다.

1
Result: 1

알다시피 자바스크립트는 변수를 선언하기 전에 사용할 수 있다. 코드 1행에서 선언하지도 않았던 a에 1을 할당했고 2행에서 콘솔에 출력한 결과가 제대로 출력되었다. 정작 변수 a는 3행에서야 나타난다.

이를 호이스팅(hoisting)이라 한다. 호이스팅의 원형 hoist는 사전적 의미로 밧줄이나 장비를 이용하여 끌어올리다는 뜻이 있다. 이렇게 자바스크립트 인터프리터는 컴파일 단계에서 변수 및 함수의 선언을 항상 컨텍스트의 상단으로 끌려올린다. 인터프리터가 선언 코드를 물리적으로 상단으로 올리는 건 아니다. 다만 변수와 함수 선언을 컴파일 단계에서 메모리에 올려두고 undefined 값을 할당한다. 그 후에 코드를 실행하기 때문에 선언이 뒤에 있더라도 선언 앞에서 해당 변수를 사용할 수 있다.

다만 선언에 강조를 한 이유는 초기화 코드는 호이스팅의 대상이 아니기 때문이다.

1
2
3
var a = 5;
console.log('a: %d b: %d', a, b);
var b = 7;

위 코드를 크롬 콘솔에서 실행하면 아래의 결과가 나온다.

1
a: 5 b: NaN

자바스크립트 인터프리터가 위 코드를 호이스팅하면 아래와 같은 형태로 코드를 읽어들인다.

1
2
3
4
5
var a;
var b;
a = 5;
console.log('a: %d b: %d', a, b)
b = 7;

즉 인터프리터는 컴파일 단계에서 변수 a와 b를 메모리 상에 올려두고 undefined 값을 할당해 둔다. 다만 호이스팅은 선언만을 대상으로 하기 때문에 초기화 구문은 해당되지 않는다. 따라서 console로 값을 찍을 무렵에는 a에는 5만 할당되어 있고 b는 undefined 상태가 된다.

함수도 호이스팅의 영향을 받는다. 함수 선언식이 호이스팅의 영향을 받는다. 자바스크립트에서 함수를 사용하는 방식을 잠깐 설명하자면 자바스크립트에서 함수를 사용하는 방식은 함수 선언식(Function Declaration)과 함수 표현식(Function Expression)으로 구분할 수 있다.

함수 선언식은 다른 프로그래밍 언어에서 사용하는 함수 선언식과 동일하다.

1
2
3
function functionDeclaration() {
// 함수 내용
}

함수 표현식은 아래와 같다. Go언어같은 최신 언어에서는 자바스크립트와 같은 함수 표현식을 많이 지원하기도 한다.

1
2
3
var functionExpr = function() {
// 함수 내용
}

예를 들어 아래와 같은 코드가 있다면,

1
2
3
4
5
6
7
8
9
10
funcDecl();
funcExpr();

function funcDecl() {
return 'decl!';
}

var funcExpr = function() {
return 'expr!';
}

호이스팅때문에 인터프리터는 아래와 같이 해석한다.

1
2
3
4
5
6
7
8
9
10
function funcDecl() {
return 'decl!';
}
var funcExpr;
funcDecl();
funcExpr();

funcExpr = function() {
return 'expr!';
}

따라서 funcExpr()를 실행할 시점에는 funcExpr()이 정의되지 않았기 때문에 크롬에서 실행하면 아래와 같은 결과가 나온다.

1
Uncaught TypeError: funcExpr is not a function

이 호이스팅 때문에 의도하지 않은 버그를 내기도 한다. 문제는 자바스크립트 인터프리터의 동작을 이해하지 못하는 사람이 코드를 짜면 버그가 잘 날 수 있다는 사실, 그리고 그 버그가 나오면 찾기 어렵다는 문제가 있다. ~C계열의 undefined behavior의 향기가…
따라서 호이스팅을 막으려면

  1. 선언을 함수나 전역 상단에 적기
  2. let 사용
  3. strict 모드 사용 - use strict 을 사용 시 선언하지 않은 변수를 사용할 수 없다.
공유하기 댓글