JavaScript 호이스팅과 TDZ는 무엇일까?(1)

Choi·2022년 5월 18일

Javascript

목록 보기
7/9
post-thumbnail

📝 JavaScript 호이스팅이란?

공식문서에서는 호이스팅을 아래와 같이 정의한다.

JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다. var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화합니다. 반면 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않습니다.
호이스팅을 설명할 땐 주로 "변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는" 것으로 말하곤 합니다. 따라서 변수를 정의하는 코드보다 사용하는 코드가 앞서 등장할 수 있습니다. 다만 선언과 초기화를 함께 수행하는 경우, 선언 코드까지 실행해야 변수가 초기화된 상태가 됨을 주의하세요.

쉽게 말해 스코프 안에 있는 선언들(변수명, 함수명)을 최상단으로 끌어올려두는 것을 의미한다. 호이스팅은 자바스크립트 인터프리터가 코드를 해석할 때 '변수, 함수의 선언 처리' & '실제 코드 실행' 두 단계로 나누어 처리하기 때문에 발생하는 현상이다.

호이스팅은 변수의 선언과 초기화를 분리하여, 선언만 코드의 최상단으로 끌어올려주기 때문에 변수를 정의하는 코드보다 사용하는 코드가 앞서 등장할 수 있다.

JavaScript는 실행 컨텍스트를 생성하면서 코드에 관련한 식별자, 환경 정보 등을 수집한다.
따라서, 코드가 실행되지 않았어도 JavaScript 엔진은 환경에 속한 변수명들을 모두 알 수 있는데 이러한 특징을 지닌 것이 바로 호이스팅(hoisting)이다.

  • Function의 호이스팅
함수는 코드를 실행하기 전에 함수 선언에 대한 메모리부터 할당되어 함수를 호출하는 코드를 함수 선언 앞에 배치할 수 있다.

그러나 예상치 못한 에러가 발생할 수 있으므로 const를 사용한 화살표형 함수 선언 방식을 사용하는 것이 좋다.

  • 변수 선언 방식(var)의 호이스팅
변수 선언 시 초기화를 제외한 선언만 호이스팅한다.

변수를 먼저 사용하고 그 후에 선언 및 초기화가 나타나면 사용하는 시점의 변수는 undefined = 기본 초기화 상태가 된다.
let 과 const로 선언한 변수도 호이스팅이 되긴 하지만, var와 달리 undefined로 변수를 초기화하지 않기 때문에, 변수 선언 전에 먼저 사용하면 오류가 발생한다. 리터럴 값은 호이스팅이 되나 특별한 이유로 인해 '초기화가 필요한 상태'로 관리되는 것이다. 이는 '선언은 되어있지만 초기화가 되지 않아 변수에 담길 값을 위한 메모리에 할당되지 않은 상태'라고도 말할 수 있다.

📝 JavaScript TDZ란?

TDZ는 Temporal Dead Zone의 약자로 일시적인 사각지대라는 뜻이다. 이 일시적인 사각지대는 스코프(Scope)의 시작지점부터 초기화 시작지점까지의 구간을 TDZ라고 일컫는다.

자바스크립트에서 변수는 선언, 초기화, 할당 3가지의 단계를 거쳐 생성된다.

  • 선언 단계(Declaration phase): 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계를 의미하며 이 변수 객체는 스코프가 참조하는 대상이 된다.

  • 초기화 단계(Initialization phase): 실행 컨텍스트에 존재 하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계로 이 단계에서 할당된 메모리에는 undefined로 초기화 된다.

  • 할당 단계(Assignment phase): 사용자가 undefined로 초기화된 메모리의 다른 값을 할당하는 단계.

이 3가지 단계의 순서는 var와 let/const에 차이를 준다.

1) var: var키워드 변수는 변수 선언 전 선언 단계와 초기화 단계를 동시에 진행한다. 자바스크립트는 실행 컨텍스트 변수 객체의 변수를 등록하고 메모리를 undefined로 만들어 버리는데 그 때문에 변수를 선언하기 전에 호출을 해도 undefined로 호출이 되는 호이스팅이 발생하게 되는 것이다.

2) let: let으로 선언된 변수는 var 키워드와는 다르게 선언단계와 초기화 단계가 분리되어서 진행이 된다. 그렇기 때문에 실행 컨텍스트에 변수를 등록했지만, 메모리가 할당이 되질 않아 접근할 수 없어 참조 에러(ReferenceError)가 발생하게 된다.
사람들은 이런 현상을 보고 '호이스팅이 되질 않는다'고 생각하게 된 것이다.
let 또한 선언 전 실행 컨텍스트 변수 객체에 등록이 되어 호이스팅이 되지만 이 TDZ 구간에 의해 메모리가 할당되지 않아 참조 에러(ReferenceError)가 발생한다.

📝 JavaScript Scope란? (+Scope chain)

  • Scope
    변수에는 유효 범위가 있다. 변수와 같은 식별자에 대한 유효 범위를 스코프라고 한다.
    이런 유효범위는 전역 공간과 지역 공간으로 나뉘며, 이에 따라 변수도 전역 변수와 지역 변수로 나뉜다.
Global variable (전역 변수)

스크립트 영역 어디에도 힘을 발휘할 수 있는 변수이다.
변수가 어떤 블록에도 들어가있지 않을 경우 전역변수이다.

Local variable (지역 변수)

정해진 영역에서만 힘을 발휘하는 변수이다.
보통 블록{} 안이나, 함수 안에서만 변수에 접근할 수 있다.

  • Scope Chain
    식별자의 유효 범위, 즉, scope를 안에서부터 밖으로 검색해 나가는 것을 scope chain이라고 한다.
    특정 변수나 함수를 사용하려고 할 때, 먼저 자신이 속한 중괄호 안에서 해당하는 변수가 있는지 찾고, 없으면 바깥쪽의 가장 가까운 중괄호 안에서부터 찾는다.

var x = 3;  //전역공간 변수 
var outer = function () {
    var inner = function () {
        console.log(a)   //undefinde
        var x = 5  //지역변수 
    }
    inner()
    console.log(x) //결과 3
}
outer()
console.log(x)  //결과 3

컨텍스트는 해당 코드의 외부 환경 정보를 수집해올 때 scope chain이 발생한다.

바로 위 식별자의 Environment를 참조해오기 때문에 위 예제에서 x가 1이 된다.

☝ 먼저 outer가 외부 환경의 정보를 수집하는 과정에서 전역 공간의 변수 a를 수집해온다.

✌ 다음으로 inner는 outer의 Environment를 참조해서 외부환경의 정보를 수집해온다.

그렇게 전역 공간의 x를 가져오게 되는 것이다.

이렇게 바로 위 식별자의 Environment를 참조하는 것이 scope chain이다.

profile
1101100100011

0개의 댓글