본문 바로가기
Front-End/JavaScript

[JS] 호이스팅과 TDZ (Temporal Dead Zone) - let도 호이스팅이 된다!

by 연제원 2021. 6. 9.

처음 자바스크립트를 공부하면서 호이스팅이란 개념을 들었을 때 '단순히 끌어올린다'라는 생각만 했었다. 맞는 말이지만, 실제로 아래 코드를 실행해보면 다음과 같은 결과가 일어난다.

console.log(success); // undefined;
console.log(fail); // ReferenceError

var success = '성공!';
let fail = '실패 ㅠ'

var을 사용한 경우는 문제가 없고, let을 사용한 경우는 에러가 발생한 것을 보니까 var은 호이스팅이 되고 let은 호이스팅 되지 않는 것일까? 근데 호이스팅이 되면 끌어올려지니까 let을 사용한 경우는 그렇다치고, var로 선언한 success는 접근할 수 있는데 왜 값은 undefined지?

의문점들이 막 생겨났다. 이러한 상황이 발생하는 이유를 한번 알아보자!

Hoisting (호이스팅)


호이스팅에 대해 간단히 요약해보자면 모든 선언들을 모두 끌어 올려서 해당 유효 범위(스코프)의 최상단에 선언하는 것을 의미한다.(이때 변수의 스코프에 따라 전역 스코프일 경우 스크립트 단위의 최상단으로 끌어올려지고, 함수 범위일 경우 함수 내 최상단으로 끌어 올려진다.)

여기서 중요한 점은 선언된 것을 끌어올린다는 것과 선언과 할당된 값을 전부 끌어올리는 것이 아니라 선언만 끌어올려진다. 

 

하나의 예로 자바스크립트의 경우 함수를 생성할 때 선언, 표현 2가지 방식으로 함수를 생성할 수 있다.

declaration(); // "선언식!"
expression(); // ReferenceError: Cannot access 'expression' before initialization

function declaration() {
  console.log("선언식!");
}

// 함수 표현식
const expression = function() {
  console.log("표현식!");
}

이때 선언으로 생성된 함수만이 끌어올려져서 호이스팅이 가능하다. 이제 선언된 경우에 호이스팅이 된다는 것을 기억하고 다음으로 넘어가보자!

TDZ (Temporal Dead Zone)


결론부터 말하자면 선언 단계부터 초기화 시작 전까지의 구간을 TDZ(Temporal Dead Zone)이라고 한다.

(한국말로 일시적인 사각지대라는 의미) 

자바스크립트에서 변수는 기본적으로 다음 3단계를 거쳐 생성이 된다.

 

  1. 선언 단계 (Declaration phase) 
    변수를 실행 컨텍스트의 변수 객체에 등록하는 단계를 의미
    이 변수 객체는 스코프가 참조하는 대상이 된다.
  2. 초기화 단계 (Initialization phase)
    실행 컨텍스트에 존재 하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계
    이 단계에서 할당된 메모리에는 undefined로 초기화된다.
  3. 할당 단계 (Assignment phase)
    사용자가 undefined로 초기화된 메모리의 다른 값을 할당하는 단계

* 실행 컨텍스트 : 코드가 실행되기 위해 필요한 환경

* 스코프 : 변수가 값을 참조할 때 접근할 범위

 

그렇다면 위에서 예시를 든 var, let은 어떤 차이가 있을까?

 

var의 경우

 

사진과 함께 단계별로 보자

  1. var 키워드 변수는 선언 시 선언초기화를 동시에 진행
  2. 자바스크립트는 실행 컨텍스트 변수 객체의 변수를 등록하고 메모리를 undefined로 만듦
  3. 변수에 값을 할당
  4. 변수는 값을 가지게 된다.

1, 2번 과정을 보면 선언과 동시에 초기화를 진행하며 메모리를 할당해주기 때문에 호이스팅이 될 때 참조가 가능해서 오류가 발생하지 않는 것이다!

let의 경우

let (+ const)의 경우 var와 달리 선언초기화를 따로 진행을 한다. 그렇기 때문에 선언단계에서 실행 컨텍스트에 변수를 등록했지만, 메모리가 할당되지 않아 접근할 수 없어서 ReferenceError가 발생한다. 즉 호이스팅은 되지만 접근할 수가 없어서 호이스팅이 되지 않는 것 처럼 보이는 것이다.

 

함수선언식의 경우

추가적으로 함수 선언식의 경우 선언, 초기화, 할당동시에 진행되기 때문에 호이스팅도 가능하고 실행도 가능하다.

 

정리


처음에 생겼던 의문점에 대해 정리를 해보자면 호이스팅은 선언된 것들을 최상단으로 끌어올리는 행위를 말한다. 이때 변수에 어떤 값이 할당되기 전인 선언된 상태만 끌어올려진다. 그렇기 때문에 제일 처음의 예시 중 var로 선언과 할당을 했던 success는 호이스팅이 됐지만 undefined로 값이 나타났던 것이다.

그리고 선언된 변수나 함수들을 최상단으로 끌어올려지는데 선언할 때 TDZ가 있는 즉, 스코프의 시작 지점부터 초기화 시작 지점까지의 구간이 있는 let의 경우(선언과 초기화가 따로 진행되는 경우), 호이스팅은 되지만 참조할 메모리가 없어 ReferenceError가 발생한다!

 

몇가지 예시를 들어보자면 다음과 같다.

  • 호이스팅은 되지만 참조오류가 생기는 경우
    let, const, class구문, class construtor()내부의 super()메소드 (상속받은 경우), 기본 매개변수(ES6)
  • 호이스팅도 되고 참조오류도 생기지 않는 경우
    var, 함수 선언식, import

 

어떻게 보면 상당히 불편하다고 느낄 수 있지만, 반대로 생각해보면 선언전에 변수를 사용해서 발생할 예상치 못한 위험들을 줄이고 안전한 코드를 짤 수 있게 도와준다는 생각이 든다. 확실한 것은, let과 const 및 TDZ가 존재하는 선언부들을 사용할 때 TDZ에 대한 개념을 잘 알고 있다면, 참조오류가 발생하는 것 이외에 여러 부작용을 방지할 수 있을 것이다!

 

더 공부할 것


추가로, 이 내용을 찾다보니 호이스팅이 가능한 원리는 컴파일 단계에서, 우리가 작성한 코드가 실행되기 바로 직전에 변수와 함수의 선언부들이 스캔되고, 스캔된 변수와 함수 선언부들이 자바스크립트 자료구조 메모리에 추가된다고 한다.

그런데 자바스크립트는 인터프리터 언어인데 컴파일 과정을 거친다..? 간략하게 보긴봤지만 이 또한 추후에 자세히 정리를 해봐야겠다!

 

 

 

 

 

댓글