Hoisting

체리·2025년 9월 7일

JavaScript

목록 보기
1/1

호이스팅


변수, 함수 선언이 실행되기 전에 메모리에 먼저 올라가는 동작
코드가 작성된 위치와 관계없이 선언 부분이 최상단으로 끌어올려진 것처럼 동작

왜 호이스팅이라는 것이 존재?

자바스크립트 엔진은 코드를 위에서 아래로 단순히 한 줄씩 실행하지 않고, 먼저 실행 컨텍스트를 만들고 내부에 변수를 등록한다.

📌 실행 컨텍스트?
자바스크립트 코드가 실행되는 환경을 추상화한 개념으로, 자바스크립트의 동작 원리를 담고 있는 핵심 개념
자바스크립트 엔진은 실행 컨텍스트를 통해 식별자와 스코프를 관리

선언문이 코드 아래쪽에 있어도, 실행 시점에는 이미 메모리에 올라와 있어 접근이 가능

개발자가 보기에 “끌어올려진 것처럼 보이는 현상”이라서 Hoisting이라는 이름이 붙었다.

다른 언어와 비교

  1. 일반적인 컴파일 언어(C, Java, C# 등)
  • 코드를 실행하기 전에 전체 소스를 먼저 컴파일
  • 컴파일 과정에서 이미 변수와 함수, 클래스 선언을 다 파악해 놓기 때문에, 실행 시점에서는 “선언이 위에 있느냐 아래 있느냐”가 문제가 되지 않음
    int main() {
        printf("%d\n", x); // ❌ Error: undeclared identifier
        int x = 10;
    }
    선언 전에 변수를 쓰면 바로 컴파일 에러, 실행까지 가지도 못함
  1. 인터프리터 언어(Python, Ruby 등)
  • 위에서 아래로 한 줄씩 실행하기 때문에 선언이 나오기 전에는 참조할 수 없음
    print(x)   # ❌ NameError: name 'x' is not defined
     x = 10
    선언이 나오기 전에는 참조하게 된다면 런타임 에러 발생
  1. 자바스크립트
  • 인터프리터 언어처럼 보이지만, 사실은 실행 전에 코드를 한 번 훑어서 실행 컨텍스트 준비
  • 변수와 함수 선언을 미리 등록하기 때문에, 실행 시점에 코드 순서와는 다른 결과
    console.log(x); // undefined
     var x = 10;
    다른 언어라면 에러가 나야 하지만, var 키워드로 선언한 변수는 어떠한 값도 할당하지 않아도 “호이스팅” 덕분에 undefined가 출력

변수 호이스팅

자바스크립트 엔진은 변수 선언을 3단계에 거쳐 수행

  1. 선언 단계 - 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.
  2. 초기화 단계 - 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.
  3. 할당 단계 - undefined로 초기화된 변수에 실제 값을 할당한다.

var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한 번에 이루어진다.

따라서 변수 선언문 이전에 변수에 접근하여도 Environment Record에 변수가 존재하기 때문에 에러가 발생하지 않고 undefined를 반환한다.

Environment Record?

실행 컨텍스트의 객체 중 변수, 매개변수, 인수, 함수 선언 같은 정보를 담는 객체

letconst는 선언 단계만 먼저 실행 컨텍스트에 등록되고 초기화 단계는 변수 선언문을 만났을 때 실행된다.

따라서 변수 선언문 이전에 변수에 접근하면 Environment Record 에 변수가 존재하지만 초기화되어 있지 않기 때문에 에러가 발생한다.

이 사이 구간을 TDZ(Temporal Dead Zone)라 부르며, 접근 시 ReferenceError 발생

TDZ(Temporal Dead Zone)?

변수가 스코프에 선언되어 존재하지만, 아직 초기화되지 않아 접근할 수 없는 구간

이 구간에서 해당 변수에 접근하면 ReferenceError(참조 에러) 발생

  • var: 선언과 초기화(undefined)까지 호이스팅됨 → 선언 전에 접근 가능하지만 undefined 반환
  • let/const: 선언만 호이스팅됨. 초기화되기 전까지 TDZ에 묶여 있어 접근 시 ReferenceError(참조 에러)
console.log(a); // undefined
var a = 10;

console.log(b); // ReferenceError (TDZ)
let b = 20;

함수 호이스팅

  • 함수 선언문 전체가 호이스팅됨 (선언 + 함수 본문) → 선언 전에 호출 가능
    sayHi(); // "Hi"
    
    function sayHi() {
      console.log("Hi");
    }
    실행 컨텍스트 생성 단계에서, 함수 선언문은 함수 객체 자체가 메모리에 저장되기 때문
  • 함수 표현식 (var, let, const) 변수 호이스팅 규칙을 따름
    • var
      - 준비 단계: var name만 등록 + undefined로 초기화
      - 실행 단계: 대입문 도달 시 함수 객체가 그때 들어감
      - 선언 전 호출 → TypeError: name is not a function (이름은 있지만 값이 아직 undefined)

      greet(); // TypeError: greet is not a function
      var greet = function() { console.log("Hello"); };
    • let, const
      - 준비 단계: 이름만 등록(TDZ)
      - 실행 단계: 선언문에서 초기화 + 대입
      - 선언 전 접근/호출 → ReferenceError (TDZ)

      greet(); // ❌ ReferenceError
      let greet = function () {
        console.log("Hello");
      };

무엇을 쓰는게 좋은가?


ECMAScript, ESLint, 코드 컨벤션을 참고하였음

변수

  • 기본은 const
    • 재할당 필요 없으면 무조건 const로 고정 → 의도 명확, 실수 줄임.
  • 필요할 때만 let
    • 루프 카운터, 상태 갱신처럼 정말 재할당이 필요할 때만 사용.
  • var는 금지

함수

  • 모듈/파일 상단의 재사용 유틸함수 선언문 사용 권장
    • 의도적으로 “어디서든 호출 가능”하게 하고 싶을 때만.
    • 테스트/유틸·순수 함수
  • 그 외 대부분const 함수 표현식(또는 화살표 함수)
    • 선언 순서를 지키게 되어 의존 관계가 명확.
    • 블록 스코프 보장(TDZ로 선언 전 사용 방지).
  • 블록 내부의 함수 선언문은 지양

0개의 댓글