프로그래밍/JavaScript

[JS] 호이스팅이 발생하는 이유

mhko411 2021. 7. 6. 13:53
728x90
호이스팅으로 인해 변수에 값을 할당하기 전에 참조할 수 있다. 참조 후에 변수를 선언하더라도 코드가 실행되면 변수를 맨 위로 끌어올린 후에 실행되기 때문에 이러한 현상이 발생한다. 즉 아래와 같은 코드를 호이스팅이 발생한 코드라고 할 수 있다. var로 선언한 변수 x에 도달하기 전에 참조하여 출력할 수 있다. 이때 x에는 undefined가 저장되어있을 것이다.
console.log(x); // undefined가 출력된다.
var x = 5;

 

호이스팅이이 무엇인지는 알 수 있다. 하지만 호이스팅이 왜 발생하는지 알아볼 필요가 있다.


자바스크립트가 코드를 실행하는 방법

먼저 자바스크립트가 코드를 어떻게 실행하는지 이해할 필요가 있다. 자바스크립트 엔진은 소스코드를 평가하고 실행하는 2가지의 과정으로 진행된다.

 

소스코드의 평가 과정에서는 선언문으로 작성된 변수와 함수 등을 실행하여 변수와 함수 식별자를 키로 하여 실행 컨텍스트가 관리하는 환경에 등록한다. (실행 컨텍스트에 대해서는 추후에 자세하게 정리할 예정. 실행 컨텍스트는 실행 가능한 코드가 되기 위해 필요한 환경)

평가 과정이 끝났다면 소스코드가 순차적으로 실행된다. 소스코드가 실행된다는 것은 런타임이 시작된다는 의미이다. 이때 어떠한 변수나 함수를 만났다면 실행 컨텍스트가 관리하는 환경에서 검색하여 참조하게 된다.

 

자바스크립트는 소스코드를 평가하고 실행하기 때문에 런타임 이전에 선언한 변수와 함수를 실행 컨텍스트가 관리하는 환경에 등록하게 된다. 이에 따라 호이스팅이 발생하게 되는 것이다. 평가 과정에서 key-value 쌍으로 변수와 함수를 등록할 때 해당 변수와 함수 식별자를 key로 하고 value는 undefined를 등록하게 된다. 그렇기 때문에 위에서 변수 x를 출력할 때 "undefined"가 출력되는 것이다.

 

var와 let, const

var로 선언한 변수와 let, const와 선언한 변수는 호이스팅에 대한 차이점이 존재한다.

var는 선언과 초기화가 동시에 진행된다. 즉 x라는 변수를 선언했다면 동시에 undefined로 초기화가 되는 것이다. 

하지만 let과 const는 선언이후에 초기화 과정이 이루어진다. var는 undefined로 초기화되었기 때문에 참조가 가능하다. 하지만 let과 const는 선언 이후에 값을 할당하기 전까지 초기화가 이루어지지 않기 때문에 참조가 불가능하다. 

 

아래와 같은 코드를 보자.

let으로 선언한 변수 foo가 선언이 되었다. 하지만 선언 이후에 초기화가 이루어지지 않았기 때문에 참조할 수 없다는 에러가 발생한다. 이처럼 선언하고 초기화 전까지를 일시적 사각지대(TDZ)라고 한다.

이후 foo를 선언하는 부분이 실행되어 undefined로 초기화가 진행된다.

// 아직 foo가 선언은 되었지만 초기화가 진행되지 않았기 때문에 참조 불가능
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization

// foo가 undefined로 초기화가 되었다.
let foo;
console.log(foo); // undefined

// foo에 값이 할당되었다.
foo = 5;
console.log(foo); // 5

 

let과 const도 호이스팅이 발생한다.

하지만 let과 const로 선언한 변수가 호이스팅이 발생하지 않는 것은 아니다. 마치 호이스팅이 발생하지 않는 것처럼 보이는 것이다. 단지 선언과 초기화 과정이 나눠지기 때문에 해당 선언문이 만나기 전까지 초기화가 되어있지 않아 참조가 불가능한 것이다. 하지만 이러한 제약으로 인해 개발자들의 실수를 줄일 수 있을 것이다.

 

다음 코드를 보자.

만약 let과 const로 선언한 변수가 호이스팅이 발생하지 않는다면 a가 undefined로 출력될 것이다. 하지만 지역 변수가 호이스팅이 발생한 것이다. 즉 블록문 내에서 지역 변수 a가 선언되었고 초기화는 진행되지 않은 것이다. 따라서 a를 출력하려고 할 때 스코프 체인으로 변수를 탐색하고 블록문에 선언한 변수 a를 찾아 출력을 하였다. 하지만 지역 변수 a는 초기화 이전 단계이기 때문에 참조가 불가능하다는 에러가 발생했다.

let a; // 전역 변수
{
  console.log(a); // ReferenceError: Cannot access 'a' before initialization
  let a = 2; // 지역 변수
}

 

'프로그래밍 > JavaScript' 카테고리의 다른 글

[JS] Closure  (0) 2021.07.10
[JS] 실행 컨텍스트  (0) 2021.07.07
[Vue] 싱글 파일 컴포넌트(SFC)  (0) 2021.05.11
[Vue] computed와 watch  (0) 2021.05.10
[Vue] Vue 인스턴스  (0) 2021.05.09