이번 포스팅에서 알아볼 내용은 자바스크립트의 중요한 개념 중 하나인 Hoisting입니다.
JavaScript에서의 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 = "호이스팅!"
다음은 변수의 선언 없이 초기화하는 경우의 예제입니다.
console.log(str)
// => ReferenceError 출력
str = "호이스팅!"
// 변수의 선언 없이 초기화
다음은 변수의 초기화가 변수 선언까지 병행하는 예제입니다.
a = '호이'
b = '스팅'
console.log(a + b)
// => 호이스팅
let과 const의 호이스팅
let과 const로 선언한 변수도 호이스팅 대상이지만, var와 다르게 호이스팅 시점에 undefined로 변수를 초기화하지 않습니다.
따라서 변수의 초기화를 수행하기 전에 실행하는 코드가 나타나면 예외( ReferenceError )가 발생합니다.
let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 *일시적 사각지대( Temporal Dead Zone : TDZ )에 빠지기 때문입니다.
*Temporal Dead Zond ( TDZ )
스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말합니다.
TDZ를 이해하기 전에 우선 변수의 생명 주기를 알 필요가 있습니다.
JavaScript에서의 변수는 3가지 단계의 LifeCycle을 거쳐 생성됩니다.
- Decalaration phase ( 선언 단계 ) : 변수를 실행 콘텍스트의 변수 객체에 등록하는 단계를 의미합니다. 해당 변수 객체는 스코프가 참조하는 대상이 됩니다.
- Initialization phase ( 초기화 단계 ) : 실행 콘텍스트에 존재하는 변수 객체에 선언 단 게의 변수를 위한 메모리를 만드는 단계입니다. 이 단계에서 할당된 변수는 메모리에 undefined로 초기화됩니다.
- Assignment phase ( 할당 단계 ) : 변수가 undefined로 초기화된 메모리에서 다른 값을 할당하는 단계입니다.
그리고 var과 let( const )에서 이러한 변수의 생명주기에 차이가 존재합니다.
다음 그림은 var으로 선언된 변수의 생명 주기입니다.
var로 선언된 변수는 변수 선언 전에 선언 단계와 초기화 단 게를 동시에 진행합니다.
따라서 JavaScript는 실행 콘텍스트 변수 객체에 변수를 등록하고 메모리를 undefined로 초기화합니다.
그렇기에 변수를 선언하기 전에 호출을 하면 undefined로 호출이 되는 호이스팅이 발생하는 것입니다.
다음은 let으로 선언된 변수의 생명 주기입니다.
let으로 선언된 변수는 var와는 다르게 선언 단 게와 초기화 단계가 분리되어 수행됩니다.
따라서 실행 콘텍스트에 변수를 등록했지만, 메모리가 초기화되지 않아 참조하거나 호출하는 경우 예외( ReferenceError )가 발생하는 것입니다.
이런 생명주기 때문에 호이스팅의 개념을 모르는 개발자는 let, const는 호이스팅이 되지 않는다라고 오해할 수 있습니다.
위에서 언급한 TDZ가 바로 let lifecycle의 선언 단계 초기화 단계의 사이 구간에 생성되는 것이라는 사실을 알 수 있습니다.
참고로 함수의 생명 주기는 다음 그림과 같이 진행됩니다.
다음은 함수 호이스팅의 예제입니다.
X1(); // 함수 선언문에서 호이스팅 현상이 발생합니다.
X2(); // 함수 표현식이라서 호이스팅 현상이 발생하지 않습니다.
function X1() {
console.log('x1 함수의 호이스팅')
}
var X2 = ()=>{
console.log('x2 함수의 호이스팅')
}
document.writeln( X1() )
document.writeln( X2() )
이번 포스팅으로 Javascript의 호이스팅에 대해 알 수 있었습니다.
'Frontend > JavaScript' 카테고리의 다른 글
[JavaScript] 프로토타입에 대해 알아보자! ( prototype ) (0) | 2022.03.04 |
---|---|
[JavaScript] 얕은 복사와 깊은 복사에 대해 알아보자! (0) | 2022.02.06 |
[JavaScript] 업로드 이미지를 Base64로 변환하기 (0) | 2022.01.14 |