인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것.
쉽게 말해, 코드를 순차적으로 실행하기 전에, 변수와 함수 선언부분을 해당 스코프의 최상단으로 끌어 올려진 것 처럼 작동하게 하는 것이다.
console.log(foo); // undefined
var foo = 123;
console.log(foo); // 123
호이스팅은 자바스크립트의 모든 변수, 함수 선언에 대해 일어난다.
단, TDZ (Temporal Dead Zone)의 영향을 받는 구문은 호이스팅이 일어나지 않는 것 처럼 보일 수 있는데, 아래의 코드에서 예시를 볼 수 있다. (TDZ에 대한 설명은 아래에서)
console.log(foo); // Uncaught ReferenceError: foo is not defined
const foo = 123;
TDZ(Temporal Dead Zone)는 ‘일시적인 사각지대’ 라는 뜻으로 직역되며, 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말한다.
자바스크립트에서 변수는 선언, 초기화, 할당 이라는 3가지 단계에 걸쳐 생성되는데, 이를 보고 아래의 let 변수의 라이프 사이클을 보면 TDZ에 대해 조금 더 쉽게 이해할 수 있다.
선언 단계 (Declaration phase)
변수를 실행 컨텍스트의 변수 객체에 등록하는 단계. 이 변수 객체는 스코프가 참조하는 대상이 된다.
초기화 단계 (Initialization phase)
실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계. 이 단계에서 할당된 메모리에는 undefined로 초기화 된다.
할당 단계 (Assignment phase)
사용자가 undefined로 초기화된 메모리에 다른 값을 할당하는 단계.
let 변수는 호이스팅 될 때 선언 단계만 실행되며, 초기화 단계는 코드가 실행될 때 일어난다.
따라서, let 변수를 초기화 하기 전에 참조하려고 하면 아직 메모리가 할당되지 않은 상태이기 때문에 참조에러가 발생한다.
console.log(foo); // Uncaught ReferenceError: foo is not defined
let foo = 123;
var와 같은 구문은 호이스팅 될 때, 선언 단계와 초기화 단계가 동시에 실행되므로 TDZ의 영향을 받지 않고 사용할 수 있다. 선언 단계와 초기화 단계가 분리되어 실행되는 즉, TDZ의 영향을 받는 구문은 다음과 같다.
console.log(foo); // Uncaught ReferenceError: foo is not defined
console.log(bar); // Uncaught ReferenceError: bar is not defined
let foo;
const bar = 123;
var, let, const 와 동일하다. (var의 경우 undefined로 초기화 되므로 ‘is not a function’ 에러 발생)
myFunc(); // Uncaught ReferenceError: myFunc is not defined
const myFunc = () => {
console.log('call myFunc');
}
const myCar = new Car('red'); // Uncaught ReferenceError: Car is not defined
class Car {
color = '';
constructor(color) {
this.color = color;
}
}
부모 클래스를 상속받았다면, constructor 안에서 super() 호출 이전의 this는 TDZ에 있다.
// Does not work
class MyCar extends Car {
constructor(color, power) {
this.power = power;
super(color);
}
}
const myCar = new MyCar('red', '300hp'); // Uncaught ReferenceError: Car is not defined
// work
class MyCar extends Car {
constructor(color, power) {
super(color);
this.power = power;
}
}
const myCar = new MyCar('blue', '300hp');
기본 매개변수는 글로벌과 함수 스코프 사이의 중간 스코프에 위치한다. 따라서, TDZ 제한이 있다.
// Does not work
const a = 2;
function square(a = a) {
return a * a;
}
square(); // Uncaught ReferenceError: Cannot access 'a' before initialization
// work
const init = 2;
function square(a = init) {
return a * a;
}
square();
다음 구문은 선언 단계와 초기화 단계가 동시에 이루어지므로 TDZ의 영향을 받지 않는다.
console.log(foo); // undefined
var foo = 123;
함수 선언문은 함수 자체가 호이스팅 된다.
myFunc(); // call myFunc
function myFunc() {
console.log('call myFunc');
}
import 구문도 호이스팅 된다.
myFunc();
import { myFunc } from "myModule";