호이스팅(Hoisting)
호이스팅(Hoisting)이란?
- 호이스팅은 코드가 실행하기 전
변수선언/함수선언이 해당 스코프의 최상단으로 끌어 올려진 것 같은 현상을 말한다. 자바스크립트 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 구분하는 과정(*실행 컨텍스트를 위한 과정)을 거친다.자바스크립트 엔진은 코드를 실행하기 전 실행 컨텍스트를 위한과정에서 모든 선언(var, let, const, function, class)을 스코프에 등록한다.- 코드 실행 전 이미 변수선언/함수선언이 저장되어 있기 때문에 선언문보다 참조/호출이 먼저 나와도 오류 없이 동작한다. (정확히는 var 키워드로 선언한 변수와 함수 선언문일 경우 오류 없이 동작한다. 이는 선언이 파일의 맨 위로 끌어올려진 것 처럼 보이게 한다.)
실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경을 의미하고 실행되기전 이러한 실행 컨텍스트 과정(코드를 구분하는 과정)을 거친다.
이 호이스팅이라는 용어를 자바스크립트 실행 컨텍스트에 의한 위에 설명한 현상을 호이스팅이라고 부른다는 것으로 이해하면 되겠다. 그 현상이란 선언이 코드 실행 보다 먼저 메모리에 저장되는 과정으로 인한 현상을 말한다.
그래서 자바스크립트 실행 컨텍스트에 대한 이해하는 것이 호이스팅을 이해하는데 도움이 된다. (scope, hoisting, this, function, closure 등의 동작원리를 담고 있는 자바스크립트 핵심원리이다. 코드를 보고 실행 컨텍스트를 이해할 수 있어야 코드 독해, 디버깅을 할수 있다.)
변수 호이스팅(var, let, const 키워드)
- 자바스크립트 모든 선언에는 호이스팅이 일어난다.
- 하지만, let과 const, class를 이용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작한다.
- var키워드로 선언한 변수와 달리 let 키워드로 선언한 변수를 선언문 이전에 참조하면 참조에러(ReferenceError)가 발생한다.
- 이는 let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 일시적 사각지대(Temporal Dead Zone; TDZ)에 빠지기 때문이다.
변수의 생성 단계
1단계: 선언 단계(Declaration phase)
- 변수를 실행 컨텍스트의 변수 객체에 등록한다.
- 이 변수 객체는 스코프가 참조하는 대상이 된다.
2단계: 초기화 단계(Initialization phase)
- 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.
- 이 단계에서 변수는 undefined로 초기화 된다.
3단계: 할당 단계(Assignment phase)
- undefined로 초기화된 변수에 실제 값을 할당한다.
var 키워드로 선언한 변수는 선언 단계와 초기화 단계가 한번에 이뤄진다. 즉 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화한다.
따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다. 이후 변수 할당문에 도달하면 비로소 값이 할당된다.
let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다. 즉, 스코프에 변수를 등록(선언 단계)하지만 초기화 단계는 변수 선언문에 도달했을 때(코드 실행 후) 이뤄진다.
초기화 이전에 변수에 접근하려고 하면 참조 에러가 발생한다. 이는 아직 변수가 초기화되지 않았기 때문이다. 즉, 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다.
따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다. 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 ‘일시적 사각지대(Temporal Dead Zone; TDZ)’라고 부른다.
호이스팅 예제
var키워드
// 코드가 실행되기 전에 호이스팅으로 인핸 스코프 최상단에 끌어올려 스코프에 등록한다.
console.log(text); // (선언 + 초기화된 상태) 따라서 접근 가능 단, undefined
text = '안녕하세요';
var text;
console.log(text); // 안녕하세요
여기서 주의해야할 점
text = '안녕하세요';이 호이스팅된 text에 할당한다고 생각하면 안된다!
자바스크립트는 선언 없이 할당하게되면 자동으로 var 키워드로 선언하게된다.
따라서 var text = '안녕하세요';와 동일하다.
let키워드
// 호이스팅은 완료되었지만, 초기화가 안된 상태이기 때문에 참조할 수 없음.
text = '안녕하세요?'; // 선언만 된 상태, 초기화가 안되었기 때문에(메모리 공간확보 및 undefined 초기화) 참조에러
let text; // 초기화 단계 실행
const키워드
// const 키워드는 애초에 재할당이 불가능하기 때문에, 선언과 동시에 할당해야한다.
const aaa; // SyntaxError: Missing initializer in const declaration 에러발생, 즉 초기화 단계가 안되었다는 말
- 함수
one(); // 함수 선언문은 호이스팅이 발생한다(O)
two(); // 함수 표현식은 호이스팅이 일어나지 않는다(X)
function one() {
console.log('one');
}
var two = function() {
console.log('two');
}