[JavaScript] 호이스팅(Hoisting)이란?

HyungJin Han·2023년 2월 10일
0

JavaScript

목록 보기
1/7
post-thumbnail

1. 호이스팅이란?

호이스팅(Hoisting)은 코드를 실행하기 전 변수선언/함수선언을 해당 스코프의 최상단으로 끌어올리는 것이 아닌, 해당 스코프를 최상단으로 끌어 올려진 것 같은 현상을 말한다.

JavaScript 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 구분하는 과정(실행 컨텍스트를 위한 과정)을 거친다.

실행 컨텍스트란?
실행 가능한 코드가 실행되기 위해 필요한 환경을 의미하고 실행되기 전, 이러한 실행 컨텍스트 과정(코드를 구분하는 과정)을 거친다.

JavaScript 엔진은 코드를 실행하기 전 실행 컨텍스트를 위한 과정에서 모든 선언(var, let, const, function, class)을 스코프에 등록한다.

코드 실행 전, 이미 변수선언/함수선언이 저장되어 있기 때문에 선언문보다 참조/호출이 먼저 나와도 오류 없이 동작한다.

정확히는 var 키워드로 선언한 변수와 함수 선언문일 경우 오류 없이 동작하며, 이는 선언이 파일의 맨 위로 끌어 올려진 것처럼 보이게 한다.

위에서 말한 호이스팅(Hoisting)이라는 현상이란 선언이 코드 실행 보다 먼저 메모리에 저장되는 과정으로 인한 현상을 말한다.

그래서 JavaScript 실행 컨텍스트에 대한 이해하는 것이 호이스팅을 이해하는데 도움이 된다.

scope, hoisting, this, function, closure 등의 동작 원리를 담고 있는 JavaScript 핵심원리이다. 코드를 보고 실행 컨텍스트를 이해할 수 있어야 코드 독해, 디버깅을 할수 있다.


2. 변수 호이스팅 (var, let, const)

JavaScript의 모든 선언에는 호이스팅(Hoisting)이 일어난다.

하지만 let, const, class를 이용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작한다.

var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.

이러한 현상은 let 키워드로 선언된 변수가 스코프의 시작에서 변수의 선언까지 일시적 사각지대(Temporal Dead Zone, TDZ)에 빠지기 때문이다.

TDZ란?
스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말하며, let 키워드로 선언된 변수 또한 선언 전, 실행 컨텍스트 변수 객체에 등록이 되어 호이스팅이 되지만, 이 TDZ 구간에 의해 메모리가 할당이 되질 않아 참조 에러(ReferenceError) 발생하는 현상을 말한다.

중요한 점은 이 호이스팅이라는 용어가 선언이 먼저 메모리에 저장되었다는 것을 의미하기 때문에 즉, 선언이 끌어올려진다는 의미이기 때문에 모든 선언은 호이스팅이 일어난다는 말은 참이 된다.

즉, 호이스팅이 파일의 맨 위로 끌어올려진 것 같은 현상을 의미할 때 선언문 이전에 참조해서 에러를 발생시킨다고 호이스팅이 일어나지 않은 것은 아니라는 의미이다.

정확히는 선언이 끌어올려진 현상이지만, 선언이 코드 실행 전에 메모리에 저장되었다는 의미이다.

하지만 이러한 오류가 나는 이유는 var 키워드는 선언과 함께 undefined로 초기화 되어 메모리에 저장되지만, letconst는 초기화 되지 않은 상태로 선언만 메모리에 저장되기 때문이다.

초기화 되지 않으면 변수를 참조할 수 없으며, 그렇기 때문에 참조 에러(ReferenceError)를 발생시키며,
letconst에도 호이스팅이 일어나기 때문에 에러를 일으키는 것이다.

예시로

let foo = 1;
{
  console.log(foo);
  // Uncaught ReferenceError: Cannot access 'foo' before initialization
  let foo = 2;
}

위의 코드에서 만약에 호이스팅이 일어나지 않는다면 에러가 발생하지 않을 것이다.

하지만 선언과 동시에 호이스팅이 되었기 때문에 블록 스코프에서 foo를 찾을 수 없다.

var foo = 1;
{
  console.log(foo); // 1
  var foo = 2;
  console.log(foo); // 2
}

위의 코드처럼 var 키워드의 경우, 초기 선언과 동시에 undefined로 초기화 되어 메모리에 저장되기 때문에 에러가 나지 않는다.


3. 변수의 생성의 단계와 호이스팅의 원리

3-1. 변수의 생성 단계

변수는 3단계에 걸쳐서 생성된다.

1단계: 선언 단계 (Declaration phase)

  • 변수를 실행 컨텍스트의 변수 객체에 등록한다.

  • 이 변수 객체는 스코프가 참조하는 대상이 된다.

2단계: 초기화 단계(Initialization phase)

  • 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.

  • 이 단계에서 변수는 undefined로 초기화 된다.

3단계: 할당 단계(Assignment phase)

  • undefined로 초기화된 변수에 실제 값을 할당한다.

3-2. 호이스팅의 원리

3-2-1. var 키워드의 변수 선언

var 키워드로 선언한 변수는 선언 단계와 초기화 단계가 한번에 이뤄지며, 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화한다.

따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않지만, undefined를 반환한다.

이후 변수 할당문에 도달하면 비로소 값이 할당된다.

3-2-2. let, const, class 키워드의 변수 선언

let, const, class 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행되며, 스코프에 변수를 등록(선언 단계)하지만, 초기화 단계는 변수 선언문에 도달했을 때(코드 실행 후) 이뤄진다.

초기화 이전에 변수에 접근하려고 하면 참조 에러(ReferenceError)가 발생하며, 이는 아직 변수가 초기화됮 않았기 때문이다.

즉, 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문에 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다.


4. 호이스팅 예제

4-1. 변수 선언에서의 호이스팅 예시

4-1-1. var 키워드의 호이스팅 예제

// 선언이 끌어올려 지는 것이지 할당이 끌어 올려지는 것이 아니다.

name = 'HHJ';
// 선언 없으면 var로 선언한 것과 동일

console.log(name); // "HHJ"
// 별도의 var 선언 없이도 작동

var name;

4-1-2. let 키워드의 호이스팅 예제

name = 'HHJ';
// 선언 없으면 var로 선언한 것과 동일하게 작동하지만, let의 호이스팅으로 인해 오류

let name;

console.log(name);
// Uncaught ReferenceError: Cannot access 'text' before initialization

4-1-3. 함수 선언의 호이스팅 예제

foo1(); // 함수 선언문에서는 호이스팅 일어남
foo2(); // 함수 표현식이라서 호이스팅이 일어나지 않음

function foo1() {
  console.log('Hello'); // "Hello"
}

var foo2 = function() {
  console.log('world');
  // Uncaught TypeError: foo2 is not a function
}

참고 사이트

HANAMON - [JavaScript] 호이스팅(Hoisting)이란?
우주개발자 블로그 - TDZ(Temporal Dead Zone)이란?
호이스팅에 대한 오해와 진실

profile
토끼보다는 거북이처럼 꾸준하게

0개의 댓글