킹갓제네럴

거지같은 JS 문법 파헤치기 - 호이스팅,TDZ(변수가 왜 거기서 나와?) 본문

개발/자바스크립트

거지같은 JS 문법 파헤치기 - 호이스팅,TDZ(변수가 왜 거기서 나와?)

KingGodGeneral 2024. 12. 31. 09:29
반응형

아잇! 호이스팅 맛 좀 볼래?

면접에서 많이 나오는 호이스팅..

호이스팅은 var, let, const별로 다르게 동작합니다.

얘네는 호이스팅 뿐만 아니라 스코프도 다르게 지정되니,

스코프 먼저 공부해야 헷갈리지 않을거에요

 

거지같은 JS 문법 파헤치기 - 스코프(변수는 어디에서 와서 언제 가는가)

JS는 다소 특별한 변수 스코프를 가지고 있습니다.다른 언어(Java 등)의 지역 변수는 블록이 끝나는 지점에서 해제되는데요,JS는 var, let, const가 다르게 동작합니다. (참고 : 여기서 말하는 블록은

kinggodgeneral.tistory.com

 

 

사전 지식 : 변수 할당 순서

변수가 선언되면 기본적으로 3단계를 거칩니다.

1. 선언(declaration) : 메모리 공간 확보
2. 초기화(initialization) : 메모리 공간을 undefined로 할당
3. 할당(assignment) : 개발자가 원하는 값을 할당

1, 2번의 시점은 JS엔진에 의해 결정되고, 이것이 호이스팅의 영향을 받습니다.

3번의 시점은 개발자가 해당 코드를 적어놓은 시점입니다.

반응형

 

var의 호이스팅

console.log(a); // ReferenceError: a is not defined

존재하지 않는 변수를 참고하면 에러가 발생하는 것이 인지상정이죠

 

console.log(a); // undefined

var a = 1;

console.log(a); // 1

그런데 이 미친 자바스크립트는 위의 코드에서 에러를 발생시키지 않습니다.

왜냐하면 var는 변수 선언(declaration)을 항상 스코프 최상단에서 수행하기 때문이죠

세부 순서를 본다면 아래와 같습니다.

// [변수 선언] : a의 메모리 공간 확보
// [변수 초기화] : a를 undefined로 초기화

console.log(a); // undefined

var a = 1; // [변수 할당] : a에 1 할당

console.log(a); // 1

 

이와 같이 [변수 선언]과 [변수 초기화]가 스코프 최상단으로 올라가서 실행되는 것을 호이스팅이라고 합니다.

변수가 여러 개 있더라도 아래와 같이 동작합니다.

// [변수 선언] : a, b의 메모리 공간 확보
// [변수 초기화] : a, b를 undefined로 초기화

console.log(a); // undefined
console.log(b); // undefined

var a = 1; // [변수 할당] : a에 1 할당
var b = 2; // [변수 할당] : b에 2 할당

console.log(a); // 1
console.log(b); // 2

 

누구나 이걸 보면 뭔 이상한 정책을 만들어놨냐고 욕할겁니다.

가독성도 떨어지고 언젠가 오류가 날 게 뻔해 보이죠

호이스팅이 계속되면.. 그게 권리인 줄 알아요

JS도 이걸 알았는지, ES6에서 let, const를 통해 해결책을 내놓습니다.

 

let의 호이스팅

console.log(a); // ReferenceError: Cannot access 'a' before initialization

let a = 1;

console.log(a); // 1

let은 할당 이전 변수의 참조를 막아놨습니다.

어 그러면 호이스팅이 이제 없어진건가요?

- 네 아닙니다.(여전히 존재)

 

// [변수 선언] : a의 메모리 공간 확보

let a = 1; // [변수 초기화 & 할당] : a를 undefined로 초기화 한 후, 1을 할당

console.log(a); // 1

위와 같이 초기화 시점을 밑으로 끌어내린 것 뿐입니다 : 스코프 최상단 -> 할당 직전

그러면 [변수 선언]과 [변수 초기화] 사이에 틈이 생기게 되는데,

이 구간을 TDZ(Temporal Dead Zone)라고 부릅니다.

TDZ에서 변수를 참조하면 ReferenceError가 발생하는거죠

 

const의 호이스팅

const는 간단하게 [변수 선언], [변수 초기화], [변수 할당]이 무조건 동시에 발생합니다.

console.log(a) // ReferenceError: Cannot access 'a' before initialization

const a = 1; // [변수 선언&초기화&할당]

console.log(a) // 1

그리고 재할당이 금지되기 때문에 상수처럼 쓸 수 있다는 특징이 있죠

 

추가

JS엔진은 hoisting될 변수를 어떻게 알고있나요?

JS엔진은 소스코드를 실행하기 전에 평가 과정을 거칩니다.

사실 인터프리터지만.

따라서 코드 실행 전에, 블록 내부에 있는 모든 변수를 끌어모아 호이스팅 하기 때문에

호이스팅과 런타임은 관련이 없습니다.

자세한건 제일 마지막에 퀴즈 2번을 참고하세요

 

재선언

var는 재선언이 허용됩니다.

console.log(a); // undefined
var a = 1;
console.log(a); // 1
var a = 2;
console.log(a); // 2

[변수 선언]과 [변수 초기화]는 호이스팅되고,

var a = 1; 시점에서는 [변수 할당]만 진행되니,

이미 선언되었든 아니든 JS엔진은 알빠가 아니죠

다만 let과 const는 재선언이 불가능합니다.

let a = 1;
console.log(a);
let a = 2; // SyntaxError: Identifier 'a' has already been declared
console.log(a);

 

퀴즈

1번 문제 : let의 스코프와 호이스팅을 이해했나요?

let a = 1;

{
    console.log(a); // 결과값은?
    let a = 2;
}
더보기

정답 : ReferenceError: Cannot access 'a' before initialization

let으로 선언된 변수는 블록 스코프를 따르고, 그 스코프의 최상단에서 호이스팅이 발생합니다.

// [변수 선언] : outer a의 메모리 확보
let a = 1; // [변수 초기화 & 할당] : outer a를 초기화 및 1 할당

{
    // [변수 선언] : inner a의 메모리 확보
    
    console.log(a); // inner a를 TDZ에서 참조 -> 에러
    
    let a = 2; // [변수 초기화 & 할당] : inner a를 초기화 및 2 할당
}

 

2번 문제 : var의 스코프와 호이스팅을 이해했나요?

console.log(a);
if (false) {
    var a = 1;
}
console.log(a);
더보기

정답 : undefined, undefined

var로 선언된 변수는 함수 스코프를 따라 if블록을 무시합니다.

따라서 호이스팅에 의해 [변수 선언], [변수 초기화]가 발생하지만,

[변수 할당]은 런타임에서 발생하지 않습니다.

// [변수 선언 & 초기화] : a의 메모리 공간 확보 및 undefined로 초기화
console.log(a);
if (false) {
    var a = 1; // [변수 할당] : if문에 의해 실행되지 않음
}
console.log(a);

 

반응형
Comments