호이스팅
자바스크립트의 모든 변수는 호이스팅된다. 호이스팅이란 변수, 함수, 클래스 선언을 스코프의 최상단으로 위치시키는 것을 의미한다.
변수를
var
로 선언할 때는 선언함과 동시에undefined
라는 값으로 초기화된다.let
또는const
로 선언할 때에는 초기화가 되지 않기 때문에ReferenceError
에러가 발생한다.- 만약 변수가 호이스팅되지 않는다면
선언되지 않은 변수입니다.
라는 식의 에러가 발생했을 것이다.
- 만약 변수가 호이스팅되지 않는다면
함수 선언문도 호이스팅된다.
함수 표현식에서는 함수의 이름만 호이스팅되고 함수 자체는 정의하기 전에는 호출할 수 없다. 옵셔널 체이닝을 사용해서 에러가 발생하는 것을 방지할 수 있다.
- 호이스팅된 함수의 이름을
typeof
로 타입을 확인해보면undefined
라는 값을 얻을 수 있다.
- 호이스팅된 함수의 이름을
console.log(num); // ReferenceError
let num = 100;
console.log(num); // 100
console.log(typeof introduce === "undefined");
introduce("Jane Doe"); // introduce is not a function
introduce?.("Jane Doe");
var introduce = function (name) {
const message = `Hello, I am ${name}`;
console.log(message);
return message;
};
introduce("John Doe");
예시
예전에는 호이스팅에 대해서 깊게 관심가지지 않았고 let
, 또는 const
변수는 호이스팅이 되지 않는 것으로 알고 있었다. 프로젝트를 진행하면서 변수 선언 및 초기화 관련 에러를 가끔씩 마주치게 되었고 호이스팅에 대해서 다시 정리하게 되었다.
다음과 같은 상황에서도 ReferenceError
가 발생한다.
let num = 10;
function increment() {
num++;
console.log(num);
let num = num + 100;
console.log(num);
}
increment();
increment
함수에서 num++
은 함수 바깥 스코프에 선언된 변수의 값을 1 증가시키는 것으로 예상했었다.
함수 내부에서 num
이 선언되었기 때문에 변수는 함수 최상단에 호이스팅되고, num++
으로 값을 증가시키려고 하면 아직 초기화가 되지 않았기 때문에 역시 ReferenceError
가 발생하는 것이다.
에러가 발생하지 않게 하려면 함수 최상단에서 변수 초기화까지 완료시키거나, 변수 선언문을 제거하여 바깥 스코프의 변수를 참조시킬 수 있다.
let _num = 10;
function increment() {
let num = _num;
num++;
console.log(num);
num = num + 100;
console.log(num);
}
increment();
let num = 10;
function increment() {
num++;
console.log(num);
num = num + 100;
console.log(num);
}
increment();
TDZ
자바스크립트의 변수가 호이스팅되기 때문에 TDZ
라는 개념이 존재한다.
TDZ(Temporal Dead Zone)는 스코프의 시작 지점에서 변수가 초기화되기까지의 구간을 의미하며, 이 구간에서는 변수에 접근할 수 없다.
렉시컬 스코프가 함수가 작성된 위치에 따라 결정되는 반면, TDZ는 각 코드가 작성된 위치보다는 실행 시점에 따라 결정된다.
john
변수는 함수 선언문 다음에 선언 및 초기화되도록 작성되어 있다.호이스팅에 의해 변수 선언문이 최상단에 위치하게 된다. 따라서
greeting
함수 스코프에서는 선언된john
변수를 참조할 수 있다.greeting
함수가 호출되는 시점에는 변수가 초기화까지 되었기 때문에 에러가 발생하지 않는다.
function init() {
const greeting = () => {
const message = `Hello, I am ${john.name}`;
console.log(message);
return message;
};
const john = {
name: "John Doe",
age: 30,
region: "US",
};
greeting();
}
init();
상속되는 클래스의 경우 constructor
함수에서 super
함수가 호출되기 전까지는 this
에 접근할 수 없다. super
를 통해 원본 클래스에서 생성자를 호출하여 퍼블릭 속성, 메소드를 this에 바인딩하고 나서 상속 클래스 생성자에서 this에 접근 및 변경할 수 있다.
class Person {
public name: string;
public age: number;
public region: string;
constructor(name: string, age: number, region: string) {
this.name = name;
this.age = age;
this.region = region;
}
}
class Employee extends Person {
public company: string;
public department: string;
constructor(
name: string,
age: number,
region: string,
company: string,
department: string
) {
this.company = company; // ReferenceError
this.department = department; // ReferenceError
super(name, age, region);
}
}
class Employee extends Person {
public company: string;
public department: string;
constructor(
name: string,
age: number,
region: string,
company: string,
department: string
) {
super(name, age, region);
this.company = company;
this.department = department;
}
}