호이스팅과 선언 초기화 방식
선언보다 앞에서 변수를 읽을 때 var 는 undefined, let 은 ReferenceError, 함수 선언은 정상 호출되는 이유를 선언별 초기화 방식으로 정리한다.
Seobway · · 7분
문제 상황
아래 세 코드는 모두 선언보다 앞에서 식별자를 참조하지만 결과가 각각 다르다.
console.log(a); // undefined
var a = 1;
console.log(b); // ReferenceError
let b = 1;
hello(); // "안녕"
function hello() { console.log("안녕"); }
var 는 값 없이 읽히고, let 은 에러가 나고, 함수 선언은 정상 호출된다.
이 차이는 선언 종류마다 초기화 시점이 다르기 때문에 생긴다.
동작 원리
자바스크립트 엔진은 함수나 블록을 실행하기 전에 생성 단계를 거친다. 이 단계에서
스코프 안의 선언을 모두 찾아 환경 레코드(Environment Record)에 식별자를 등록한다.
코드를 한 줄씩 실행하는 것은 그다음이다. 선언이 코드 맨 위로 옮겨진 것처럼 보이는
이 현상을 호이스팅(Hoisting)이라고 부른다.
선언 종류마다 등록할 때 넣는 초기값이 다르다.
등록과 동시에 undefined 로 초기화된다. 선언 전에 읽어도 에러 없이 undefined 가 나온다.
식별자는 등록되지만 초기화되지 않은 상태로 남는다. 이 상태에서 접근하면 ReferenceError 가 발생한다.
함수 객체 전체가 생성되어 식별자에 바인딩된다. 선언보다 앞에서 호출할 수 있다.
let · const 가 등록된 시점부터 선언문이 실행되기 전까지의 구간을
TDZ(Temporal Dead Zone)라고 한다. 세부 규칙은 TDZ(Temporal Dead Zone)에서 다룬다.
인터랙티브 다이어그램: 생성 단계와 실행 단계의 식별자 상태 (단계별로 재생할 수 있다)
- 스코프에 진입하면 선언을 먼저 수집해 환경 레코드에 등록한다.
- var a 는 등록과 동시에 undefined 로 초기화된다.
- let b 는 등록되지만 초기화되지 않는다. 이 시점부터 TDZ 이다.
- 함수 선언은 함수 객체까지 생성되어 바인딩된다.
- 실행 단계에서 a 를 읽으면 undefined 가 반환된다.
- b 는 초기화 전이므로 ReferenceError 가 발생한다.
- hello 는 이미 함수 객체가 바인딩되어 있어 정상 호출된다.
- let b = 1 이 실행되면 b 가 초기화되고 TDZ 가 끝난다.
var 는 선언 전 접근이 undefined 로 조용히 통과하기 때문에, 잘못된 참조가 실행 후반에
다른 형태의 버그로 나타난다. let · const 의 TDZ 는 초기화 전 접근을 즉시 런타임 에러로
드러낸다.
예제
주석 처리된 줄을 하나씩 해제하면 각 경우의 에러를 확인할 수 있다.
console.log("var :", typeof a); // undefined (에러 아님)
var a = 1;
hello(); // 함수 객체가 이미 바인딩되어 있음
function hello() { console.log("fn : 안녕"); }
// 아래 두 줄을 해제하면 TDZ 에서 ReferenceError
// console.log("let :", b);
// let b = 1;
// 함수 표현식은 변수 선언 규칙을 따른다
// bye();
// var bye = function () { console.log("bye"); };
var bye = function(){} 은 함수 선언이 아니라 변수 선언이다. 생성 단계에서 bye 는undefined 로 초기화되므로 bye() 는 TypeError: bye is not a function 이 된다.
자주 하는 실수
정리
실행 전에 생성 단계에서 선언이 등록되며, 등록 시 초기값이 var 는 undefined,let · const 는 초기화 안 됨, 함수 선언은 함수 객체로 서로 다르다.let · const 가 초기화되기 전까지의 구간이 TDZ 다.
-
var는undefined,let은 에러인 이유를 말할 수 있다 - 함수 선언과 함수 표현식의 차이를 안다
- TDZ 가 초기화 전 접근을 에러로 만드는 이유를 설명할 수 있다
참고
- [1] Hoisting — MDN Web Docs 용어 사전
- [2] let — Temporal dead zone (MDN Web Docs)
- [3] let and const Declarations — ECMAScript 사양
관련 글
- 스코프와 스코프 체인 → (선행 개념)
- 클로저와 렉시컬 환경 →
- TDZ(Temporal Dead Zone) → (다음 글)
- javascript
- 호이스팅
- TDZ