호이스팅


호이스팅이란?

‘hoist’(호이스트)의 사전적 의미는 끌어올리기 입니다.
호이스트는 선언과 관련이 있는데 ‘변수 선언’과 ‘함수 선언’을 끌어 올립니다.

호이스팅이란 변수 또는 함수를 선언하고 초기화했을 때 선언 부분이 최상단으로 끌어올려지는 현상을 의미합니다.

호이스팅을 변수 및 함수 선언이 물리적으로 작성한 코드의 상단으로 옮겨지는 것으로 오해할 수도 있는데, 실제로는 그렇지 않습니다. 변수 및 함수 선언은 컴파일 단계에서 메모리에 저장되지만, 코드에서 입력한 위치와 정확히 일치한 곳에 있습니다. javascript 가 어떤 코드 구분을 실행하기 전에 변수 및 함수 선언을 메모리에 저장해 동작하는 방식이지만, 쉽게 선언 부분이 최상단으로 끌어올려진다고 생각하면 됩니다.


[예제 1]


a();

function a() {
  return 'a';
}

위의 예제를 보면 함수를 작성하기 전에 함수를 호출해도 코드는 정상적으로 동작합니다.


[예제 2]


var a = 1;
console.log(a + ' , ' + b);  // '1 , undefined'
var b = 2;
console.log(a + ' , ' + b);  // '1 , 2'

// 위의 코드는 아래와 같은 방식으로 동작합니다.
var a;  // a 변수 선언
a = 1;  // a 변수에 1 할당
var b;  // b 변수 선언
console.log(a + ' , ' + b);  // '1 , undefined'
b = 2;  // b 변수에 2 할당
console.log(a + ' , ' + b);  // '1 , 2'

호이스팅은 다른 데이터 타입 및 변수와도 잘 작동합니다.
하지만 위의 결과를 보시면 아시겠지만 두번째 줄에서 b의 값은 undefined 가 나옵니다.
javascript 는 선언만 끌어올립니다. 만약 변수를 선언한 뒤 나중에 초기화시켜 사용한다면, 그 값은 일단 undefined 로 지정됩니다.


[예제 3]


console.log(a());
console.log(b());
console.log(c());

function a() {  // 함수 선언문
  return 'a';
}

var b = function() {  // 익명 함수 표현식
  return 'b';
}

var c = function c1() {  // 기명 함수 표현
  return 'c';
}

실행 결과 : Uncaught TypeError: b is not a function


위의 코드는 아래와 같은 방식으로 동작합니다.


function a() {
  return 'a';
}

var b;

var c;

console.log(a());
console.log(b());
console.log(c());

b = function () {
  return 'b';
}

c = function c1() {
  return 'c1';
}

앞서 설명한대로 호이스팅은 선언과 밀접한 관계를 가지고 있습니다.
javascript 는 선언만 끌어 올리고 그 값은 undefined 로 지정하기 때문에 b를 실행 했을 때는 b는 존재 하지만 undefined 로만 지정된 상태이므로 에러가 나게 되는 겁니다. c 또한 같은 이유로 에러가 나게 됩니다.


[예제 4]


var a = 1;
var b = 10;
(function() {
  console.log(a);
  console.log(b);
  var a = 2;
  console.log(a);
  var c = 100;
})()
console.log(c);

실행 결과 :
undefined
10
2
ReferenceError: c is not defined


위의 코드는 아래와 같은 방식으로 동작합니다.


var a;
a = 1;
var b;
b = 10
(function() {
  var a;
  console.log(a);  // undefined
  console.log(b);  // 10
  a = 2;
  console.log(a);  // 2
  var c = 100;
})()
console.log(c);  // ReferenceError: c is not defined

먼저, a와 b과 선언과 동시에 값이 할당 되고 나서 함수를 실행하게 됩니다. javascript 유효범위(scope)의 단위는 블록 단위가 아닌 함수 단위입니다. 함수 속에서도 물론 호이스팅이 일어나게 되는데 먼저 a가 선언이 되고 undefined 가 지정이 됩니다. 그래서 첫번째 console.log(a) 는 undefined 를 출력하게 됩니다. b의 경우 함수 속에 b가 존재하지 않아 위의 부모(전역) b를 참조하게 됩니다. 따라서 10이 출력됩니다. 두번째 console.log(a) 의 경우는 이미 함수 스코프 안에 a가 2로 할당 되었기 때문에 2 가 출력 되는 것이고, c의 경우 유효 범위 안에 c가 존재하지 않으므로 에러가 발생합니다.


[예제 5]


let a = 1;
let b = 10;
{
  console.log(a);
  console.log(b);
  let a = 2;
  console.log(a);
  let c = 100;
}
console.log(c);

실행 결과 : ReferenceError: a is not defined

예상과 다르게 let 또는 const 의 경우는 에러가 나게 됩니다. 그렇다면 호이스팅이 안된걸까요? 호이스팅이 안됬다면 console.log(a) 는 1이 출력 됬어야 합니다. let 이나 const 는 실제로 선언된 위치가 오기 전에는 그 변수를 호출 할 수 없습니다. 이 영역을 TDZ 라고 불려지기도 합니다. 위의 예제를 통해 아래와 같이 정리를 할 수 있습니다.

기존 var 의 경우는 변수를 호이스팅 하고 undefined 를 할당하는 반면 let 과 const 는 변수만 호이스팅 하게 되는 걸 추측 할 수 있습니다.


Reference