자바스크립트의 호이스팅

자바스크립트의 호이스팅

자바스크립트의 호이스팅에 대해서 오개념 유무를 점검하고 배운 내용을 정리했습니다.

·

3 min read

Table of contents

호이스팅

자바스크립트의 모든 변수는 호이스팅된다. 호이스팅이란 변수, 함수, 클래스 선언을 스코프의 최상단으로 위치시키는 것을 의미한다.

  • 변수를 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;
  }
}