공부 및 일상기록

호이스팅이란? 본문

개발/Javascript

호이스팅이란?

낚시하고싶어요 2023. 7. 20. 11:08

호이스팅이란?

호이스팅이란 코드가 실행하기 전 변수선언/함수선언이 해당 스코프의 최상단으로 끌어올려진것 같은 현상을 말합니다.
호이스팅이 발생하는 이유는 자바스크립트 엔진은 코드를 실행하기전 실행컨텍스트가 생성되는데 이 실행컨텍스트는 코드를 원활하게 실행될 수 있도록 환경을 만들어주는 역할입니다.

실행컨텍스트

실행 컨텍스트는 생성단계와 실행단계 두가지로 나눠볼수있습니다.

생성단계

선언문만 실행해서 환경레코드에 기록하는 단계입니다.

실행단계

선언문 외 나머지 코드를 순차적으로 실행하는 단계입니다.

실행 컨텍스트 스택

const x = 1;
function foo() {
  const y = 2;
  function bar() {
    const z = 3;
    console.log(x + y + z);
  }
  bar();
}
foo();

스크린샷 2023-03-28 오전 11 30 57

위 코드가 실행되는 시간의 흐름에 따라 실행 컨텍스트 스택에는 위 사진과 같이 실행컨텍스트가 추가(push)되고 제거(pop)됩니다.

  • #1. 자바스크립트 엔진은 먼저 전역 코드를 평가하여 전역 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 push합니다. 이때 전역변수 x와 전역 함수 foo는 전역 실행 컨텍스트에 등록됩니다. 이 후 전역 코드가 실행되기 시작하여 전역 변수 x에 값이 할당 되고, 전역함수 foo가 호출됩니다.

  • #2. 전역함수 foo가 호출되면 전역 코드의 실행은 일시 중단되고 코드의 제어권이 foo함수 내부로 이동합니다. 자바스크립트 엔진은 foo함수 내부의 함수 코드를 평가하여 foo 함수 실행 컨텍스트를 생성하고 실행컨텍스트에 push합니다. 이때 foo함수의 지역변수 y와 중첩함수 bar가 foo함수 실행 컨텍스트에 등록됩니다. 이후 foo함수 코드가 실행되기 시작하여 지역변수 y에 값이 할당되고 중첩함수 bar가 호출됩니다.

  • #3. 중첩함수 bar가 호출되면 foo 함수 코드의 실행은 일시 중단되고 코드의 제어권이 bar함수 내부로 이동합니다. 자바스크립트 엔진은 bar 함수 내부의 함수 코드를 평가하여 bar 함수 실행 컨텍스트를 생성하고 실행컨텍스트에 push합니다. 이때 bar함수의 지역변수 z가 bar함수 실행컨텍스트에 등록됩니다. 이후, bar함수 코드가 실행되기 시작하여 지역 변수 z에 값이 할당되고 console.log 메서드를 호출한 이후, bar 함수는 종료된다. (console.log메서드도 함수이므로 호출하면 실행컨텍스트를 생성하고 실행컨텍스트에 push한다. 이 설명에선 생략했다.)

  • #4. bar함수가 종료되면 코드의 제어권은 다시 foo 함수로 이동한다. 이때 자바스크립트 엔진은 bar함수 실행 컨텍스트를 실행 컨텍스트 스택에서 pop하여 제거한다. 그리고 foo함수도 더이상 실행할 코드가 없으므로 종료된다.

  • #5. foo함수가 종료되면 코드의 제어권은 다시 전역코드로 이동한다. 이때 자바스크립트 엔진은 foo함수 실행 컨텍스트를 실행컨텍스트 스택에서 pop하여 제거한다. 그리고 더 이상 실행할 전역 코드가 남아 있지 않으므로 전역 실행 컨텍스트도 실행 컨텍스트 스택에서 pop되어 실행 컨텍스트 스택에는 아무것도 남아있지 않게 된다.

이처럼 실행 컨텍스트 스택은 코드의 실행 순서를 관리한다. 소스코드가 평가되면 실행 컨텍스트가 생성되고 실행 컨텍스트 스택의 최상위에 쌓인다. 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행중인 실행 컨텍스트이다. 따라서 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트를 실행 중인 실행 컨텍스트 (running execution context)라 부른다.

호이스팅이 발생하는 이유

실행 컨텍스트 생성단계에서 선언문을 환경레코드에 기록하여 콜스택에 담아두게 되는데 이 현상때문에 호이스팅이 발생하게 됩니다.

var의 경우 선언과 동시에 undefined로 초기화 되기 때문에 선언 전에 사용하면 undefined가 환경레코드에 기록되어 아무 에러 없이 사용이 가능합니다.

let과 const의 경우 선언만 환경레코드에 기록이 되고 값은 할당 되지 않아 참조오류가 발생하는 것입니다.

또한 함수선언문으로 함수를 작성한 경우에도 완성된 함수 객체를 환경레코드에 기록해 두기 때문에 에러 없이 사용이 가능합니다.

함수 표현식과 화살표 함수

앞서 함수 선언문으로 작성한 경우에는 호이스팅되어 문제없이 사용 가능하지만, 함수 표현식이나 화살표 함수로 함수를 정의하면 함수 호이스팅이 발생하지 않고 변수 호이스팅이 발생하게 되어 해당 함수를 읽기 전까지 사용할 수 없다.

console.log(test(1, 2)); // test is not a function

var test = (a, b) => {
  return a + b;
};

위 test함수를 함수선언문으로 작성하면 콘솔에 3이 찍히지만, 위 처럼 화살표함수를 사용하는 경우 함수가 할당되지 않아서 test가 함수가 아니라는 오류가 발생한다.

TDZ

let과 const, 그리고 함수 표현식처럼 선언과 동시에 초기화나 할당이 이뤄지지 않게 되면 해당 변수에 값이 할당되는 코드를 만나기 전까지 각 변수들을 사용할 수 없다. 이 구간을 TDZ(Temporal Dead Zone)이라고 한다.

따라서 흔히 TDZ는 선언전에 변수를 사용하는 것을 허용하지 않는다 라고 표현하고 var는 변수 선언 이전에 사용이 가능하기 때문에 사용을 지양해야 한다고 한다.

참고하면 좋은 영상

https://www.youtube.com/watch?v=EWfujNzSUmw (우아한 테크 유튜브)