JavaScript Hoisting

호이스팅

Hoisting

*인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할 달 하는 것을 의미합니다.

var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화합니다.

반면에 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않습니다.

그럼 호이스팅을 설명할때는 어떻게 설명해야하나요

호이스팅은 일종의 현상으로

변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는 현상

으로 말할 수 있습니다.

즉, 변수를 정의하는 코드보다 사용하는 코드가 앞서 등장할 수 있습니다.

하지만 선언과 초기화를 동시에 수행하는 경우에 선언 코드까지 실행해야 변수가 초기화되는 상태가 된다는 것을 주의해야 합니다.

(let, const를 포함한 ECMAScript 2015 표준 명세 이전의 표준 명세에는 나타나지 않습니다.)

호이스팅이란 코드가 실행되기 전 변수의 선언 혹은 함수의 선언이 해당 스코프의 최상단으로 올려지는 현상을 말합니다.

자바스크립트 엔진은 코드를 실행하기 전에 실행 콘텍스트를 위한 과정에서 모든 선언(var, let, const, function, class)을 스코프(Scope)에 등록합니다.

코드 실행 전 이미 변수의 선언 혹은 함수의 선언이 저장되어 있기 때문에 선언문보다 참조 혹은 호출이 먼저 실행되어도 오류 없이 동작합니다. (정확히 var로 선언한 변수와 함수 선언문일 경우 해당됩니다.)


*interpreter ( 해석기 )

인터프리터는 프로그래밍 언어의 소스코드를 바로 실행하는 컴퓨터 프로그램 또는 환경을 말합니다.

원시 코드를 기계어로 번역하는 컴파일러(Compiler)와 대비됩니다.

인터프리터는 다음 항목 중에 적어도 한 가지 기능을 가진 프로그램입니다.

소스 코드를 직접 실행한다.

소스 코드를 효율적인 다른 중간 코드로 변환하고, 변환한 것을 바로 실행한다.

인터프리터 시스템의 일부인 컴파일러가 만든, 미리 컴파일된 저장 코드의 실행을 호출한다.

인터프리터는 고급 명령어들을 중간 형태로 번역한 다음 실행하는 반면에 컴파일러는 고급 명령어들을 직접 기계어로 번역합니다.

인터프리터의 장점은 기계어 명령어들이 만들어지는 컴파일 단계를 거칠 필요가 없는 점입니다.

컴파일 과정에서 명령어들의 크기가 크다면 상당한 시간이 소요될 수 있습니다.


호이스팅의 대상은 선언

JavaScript는 초기화를 제외한 선언만 호이스팅합니다. 

밑의 예시코드와 같이 변수를 먼저 사용하고 그 후에 선언과 초기화가 나타나면 사용하는 시점의 변수는 기본 초기화 상태입니다.

(var 선언 시 undefined, 이외에는 초기화하지 않는다.) 

console.log(str)
// => 호이스팅한 var 선언으로 인해 undefined이 출력됨

var str

str = "some text"

다음은 변수의 선언 없이 초기화하는 경우의 예제입니다.

console.log(str)
// => ReferenceError 출력

// 변수의 선언 없이 초기화
str = "some text"

다음은 변수의 초기화가 변수 선언까지 병행하는 예제입니다.

a = 'some '
b = 'text'

console.log(a + b) // some text

let과 const의 호이스팅

let과 const로 선언한 변수도 호이스팅 대상이지만, var와 다르게 호이스팅 시점에 undefined로 변수를 초기화하지 않습니다.

따라서 변수의 초기화를 수행하기 전에 실행하는 코드가 나타나면 예외(ReferenceError)가 발생합니다.

let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 *일시적 사각지대

(Temporal Dead Zone : TDZ)에 빠지기 때문입니다. 


Temporal Dead Zond ( TDZ )

스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말합니다.

TDZ를 이해하기 전에 우선 변수의 생명 주기를 알 필요가 있습니다. 

JavaScript에서의 변수는 3가지 단계의 LifeCycle을 거쳐 생성됩니다.

ho01

Decalaration phase (선언 단계) : 변수를 실행 콘텍스트의 변수 객체에 등록하는 단계를 의미합니다.  해당 변수 객체는 스코프가 참조하는 대상이 됩니다.

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

Assignment phase (할당 단계) : 변수가 undefined로 초기화된 메모리에서 다른 값을 할당하는 단계입니다.

그리고 var과 let(const)에서 이러한 변수의 생명주기에 차이가 존재합니다.

아래 그림은 var으로 선언된 변수의 생명주기입니다.

ho02

var로 선언된 변수는 변수 선언 전에 선언 단계와 초기화 단계를 동시에 진행합니다.

따라서 JavaScript는 실행 콘텍스트 변수 객체에 변수를 등록하고 메모리를 undefined로 초기화합니다.

그렇기에 변수를 선언하기 전에 호출을 하면 undefined로 호출이 되는 호이스팅이 발생하는 것입니다.

let의 생명 주기

ho03

let으로 선언된 변수는 var와는 다르게 선언 단계와 초기화 단계가 분리되어 수행됩니다.

따라서 실행 콘텍스트에 변수를 등록했지만, 메모리가 초기화되지 않아 참조하거나 호출하는 경우 예외(ReferenceError)가 발생하는 것입니다.

이런 생명주기 때문에 호이스팅을 모른다면 let, const는 호이스팅이 되지 않는다라고 오해할 수 있습니다.

위에서 언급한 TDZ가 let lifecycle의 선언 단계와 초기화 단계 사이 구간에 생성되는 사실을 알 수 있습니다.


Function의 생명 주기

ho04

X1(); // 함수 선언문은 호이스팅 현상이 발생합니다.

X2(); // 함수 표현식은 호이스팅 현상이 발생하지 않습니다.

function X1() {
    console.log('x1 함수의 호이스팅')
}

var X2 = ()=>{
    console.log('x2 함수의 호이스팅')
}


document.writeln(X1())
document.writeln(X2())

ho05

Copyright © Juwon. Built with Gatsby