[Computer Science] Hoisting

Hoisting 이란?

  • 호이스팅은 코드가 실행하기 전 변수선언 / 함수선언이 해당 스코프의 최상단으로 끌어올려진 것 같은 현상을 말한다.
  • scope 란?
    • 식별자 접근 규칙에 따른 유효 범위
    • 식별자(변수, 함수, 클래스)에 접근할 수 있는 범위가 존재한다.
    • 범위는 중괄호 또는 함수에 의해 나눠진다.
    • 그 범위를 스코프라고 부른다.
  • 자바스크립트 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 구분하는 과정(실행 컨텍스트를 위한 과정)을 거친다.
  • 코드 실행 전 이미 변수선언 / 함수선언이 저장되어 있기 때문에 선언문보다 참조 / 호출이 먼저 나와도 오류 없이 동작한다.
  • 실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요한 환경을 의미하고 실행되기전 이러한 실행 컨텍스트 과정을 거친다.

변수 Hoisting (var, let, const)

  • 자바스크립트의 모든 선언에는 호이스팅이 일어난다.
  • let, const, class를 이용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작한다.
  • var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.
  • 이는 let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 일시적 사각지대(Temporal Dead Zone; TDZ) 빠지기 때문이다.
  • TDZ란?
    • 스코프의 시작 지점부터 초기화 시작 지점까지의 구간
  • 예제를 통해 이해해보자
// 1번 코드
a = 2;
var a;
console.log(a); // ?

위의 코드를 실행하면 어떤 결과가 나올까?
var a 선언으로 인해 해당 변수가 재정의되어 기본값인 undefined가 출력될 것이라고 예상하겠지만, 이 코드의 실행 결과는 2이다.

// 2번 코드
console.log(a); // ?
var a = 2;

위의 코드 출력 결과는 2나 ReferenceError가 아닌, undefined이다.
이러한 동작을 살펴보기 위해선 3단계에 거친 변수의 생성 과정에 대해 살펴볼 필요성이 있다.


변수의 생성과정 3단계

1) 선언 단계 (Declaration Phase)

  • 변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.

2) 초기화 단계 (Initialization Phase)

  • 변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.

3) 할당 단계 (Assignment Phase)

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

위의 예제와 같이 var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다.

  • 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화(초기화 단계) 한다.
  • 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않고, undefined를 반환한다.
  • 이후 변수 할당문에 도달하면 비로소 값이 할당된다.

이러한 현상을 변수 호이스팅(Variable Hoisting)이라 한다.


함수 선언문 vs 함수 표현식

함수에서도 마찬가지로 호이스팅이 발생한다.
함수 foo의 선언문은 호이스팅되었기 때문에 첫째 줄에서 foo를 호출할 수 있다. 이때, 호이스팅이 스코프별로 작동한다.

foo(); 

function foo() { 
  console.log(a); // undefined 
  var a = 2; 
} 

// 위 코드의 실제 동작 
function foo() { 
  var a; 
  console.log(a); 
  a = 2; 
} 

foo();

함수 선언문은 이와 같이 호이스팅되지만, 함수 표현식은 다르다.

foo(); // TypeError : foo is not a function

var foo = () => {
  console.log(a);
  var a = 2;
}

변수 식별자 foo는 호이스팅되어 글로벌 스코프에 묶이므로 foo() 호출은 실패하지 않고, ReferenceError도 발생하지 않는다. 그러나 foo는 아직 값을 갖고 있지 않은데 반해, foo()가 undefined 값을 호출하려해 TypeError라는 에러를 발생시킨다.

📌 함수와 변수 선언문은 모두 호이스팅되지만, 함수 선언문이 먼저 호이스팅되고 다음으로 변수 선언문이 호이스팅된다는 특징이 있다.

foo(); // 1
var foo;

function foo() {
  console.log(1);
}

foo = () => {
  console.log(2);
}

자바스크립트 엔진은 위의 코드를 다음과 같이 해석한다.

function foo() {
  console.log(1);
}

foo();

foo = () => {
  console.log(2);
}

Reference

참고 : Hoisting – 용어 사전 | MDN
참고 : let, const | PoiemaWeb

좋은 웹페이지 즐겨찾기