호이스팅이란(Hoisting)
*인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다. 즉, 변수, 함수, 클래스 또는 임포트(import)의 선언문이 해당 스코프의 최상단으로 끌어올려지는 동작을 말하며, 코드 실행 전에 이러한 동작이 수행된다. 이는 JavaScript 엔진이 코드를 처리하는 동작 과정 중 하나이다. 실제로 코드가 끌어올려지는 것이 아니라, JavaScript 파서가 내부적으로 끌어올려 처리한다.
여기서 *인터프리터는 프로그래밍 언어의 소스 코드를 바로 실행하는 컴퓨터 프로그램 또는 환경을 말한다. 코드를 한 줄 한 줄씩 바로 실행해나가는 방식이다.
console.log(number); // undefined
var number;
위 코드에서 출력되는 변수 name
은 아직 선언되지 않았기 때문에 에러가 발생할 것 같지만 undefined
가 출력된다. 선언된 변수 name
은 코드 순서와 상관 없이 최상단에서 선언한 것처럼 끌어올려진다. 이것이 호이스팅이다.
console.log(number); // undefined
var number = 7;
또한, 변수의 선언만 끌어올려지므로 값을 할당해도 결과는 undefined
이다.
호이스팅이 일어나는 이유
왜 일어날까? 이를 제대로 이해하기 위해서는 변수의 생성 단계와 TDZ에 대한 이해가 필요하다.
변수 생성 단계
JavaScript 엔진에서 변수는 선언 → 초기화 → 할당 단계를 거쳐 생성된다.
단계 | 설명 |
선언 | 코드에 변수 선언이 등장하면 JavaScript 엔진은 해당 변수를 *실행 컨텍스트(Execution Context)의 *환경 레코드(Environment Record)에 등록한다. var , let , const 키워드를 사용한 변수 선언일 것이다. 변수는 이 단계에서 메모리에 할당되지 않고, 단순히 존재한다는 사실을 JavaScript 엔진에게 알려준다. |
초기화 | 선언 단계에서 생성된 변수의 메모리 공간이 생성되고 변수는 undefined 로 초기화된다. 실제 메모리 공간을 확보하는 것으로 변수에 메모리 주소값이 저장되는 단계이다. 따라서, 메모리 참조를 통해 변수에 접근할 수 있다. |
할당 | 초기화 단계에서 undefined 로 초기화된 메모리에 다른 값을 할당하는 단계이다. 즉 변수에 값을 할당하면 초기화된 undefined 값이 대체되어 실제 유효한 값이 저장된다. 변수에 값을 할당할 때는 = 연산자나 다른 할당 방법을 사용한다. |
var와 let, const
var
로 선언한 변수와 let
, const
로 선언한 변수의 호이스팅은 차이점이 존재한다.
- var
var
로 선언한 변수는 선언 단계, 초기화 단계가 동시에 진행된다. 선언 단계에서 변수 등록과 동시에 초기화 단계에서 메모리 공간도 할당 받아 undefined
로 초기화되는 것이다. 그렇기 때문에 변수에 값이 할당되기 전에 접근해도 ReferenceError
가 발생하지 않고, undefined
가 반환된다.
코드를 통해 알아보자.
console.log(number); // undefined
var number = 7;
console.log(number); // 7
var
로 선언한 변수는 선언 단계, 초기화 단계가 동시에 진행되기 때문에 number
를 출력하면 undefined
이 반환되고, 이후 할당 단계를 거쳐 7
이라는 값이 반환되는 것이다.
더 자세하게는 아래와 같은 의미를 가진다.
var number;
console.log(number); // undefined
number = 7;
console.log(number); // 7
접근 가능해진 var
로 선언한 변수는 호이스팅 되어 최상단으로 이동했고, 이때 선언과 초기화 단계가 동시에 일어났기 때문에 number
를 참조하는 시점에 undefined
가 이미 할당되어진 상태라 undefined
이 반환되는 것이다. (참조 에러가 발생하지 않는다.)
- let, const
반면에 let
, const
로 선언한 변수는 선언과 초기화 단계가 분리되어 진행된다. 선언 단계에서 변수를 등록했지만, 메모리를 할당 받지 못해 접근할 수 없다. 그래서 초기화 이전에 변수에 접근하려고 하면 참조할 메모리가 없으므로 ReferenceError
가 발생한다.
“잠깐.. 그럼 let
, const
로 선언한 변수는 호이스팅이 일어나지 않는 건가?” 라고 생각할 수 있다. 정답은 호이스팅 된다. 다만 호이스팅 되었으나 메모리가 할당되지 않아 접근할 수 없는 것이다. 이때 등장하는 개념이 TDZ이다.
TDZ(Temporal Dead Zone)
우리가 사용하려는 변수의 선언 ~ 초기화 사이의 구간을 가리키는 개념이다. 변수가 선언 되었지만 아직 메모리에 할당되지 않은 상태에서 변수에 접근할 때 발생한다. 이를 TDZ에 있다, 걸렸다고 얘기한다.
console.log(number); // Uncaught ReferenceError: number is not defined
let number = 7;
console.log(number); // Uncaught ReferenceError: number is not defined
const number = 7;
- 변수 생성 단계에서 언급했듯이
var
과 달리let
과const
는 선언 단계와 초기화 단계가 분리되어 진행된다. console.log(number);
에서 변수number
에 접근하려고 할 때 초기화 되어 있지 않은 상태이기 때문에ReferenceError
가 발생하고, 이 참조 오류가 발생하는 구간이 TDZ이다.let
과const
로 선언한 변수는 초기화 단계 이전까지 TDZ에 있다.- 결론적으로
let
,const
는 참조할 메모리가 없는 것이지 호이스팅이 일어나지 않는 건 아니다. 초기화되기 전까지 TDZ에 있기 때문에 호이스팅이 일어나지 않는 것처럼 보일 뿐이다.
정리
- JavaScript 엔진은 코드가 실행하기 전에 필요한 환경을 설정하고 변수, 함수 등의 정보를 수집하여 코드 실행 시 참조할 수 있도록 한다. ⇒
실행 컨텍스트
형성 - 이 과정에서 모든 선언(
var
,let
,const
,function
,class
)이 변수 객체 메모리에 저장된다. - 코드 실행 전 이미 변수, 함수에 대한 선언이 저장되어 있기 때문에, 선언문보다 참조 및 호출이 먼저 나와도 동작할 수 있게 된다.
- 이는 선언 구문이 스코프 최상단으로 끌어올려지는 동작이며,
호이스팅이
일어났다고 한다. var
과 달리let
,const
키워드를 사용한 변수에 접근하면ReferenceError
가 발생하고, 이 참조 오류가 발생하는 구간이TDZ
이다.