What is Hoisting

mje807·2021년 6월 25일
1

What is Hoisting(호이스팅)

호이스팅이란 단어 그대로 들어올리다, 끌어올리다 라는 뜻을 가지고 있으며 실제로 변수나 함수의 선언 이전에도 해당 함수와 변수를 사용하여도 에러가 발생하지 않는 것을 확인할 수 있습니다.

console.log(foo);   /// undefined
bar();              /// undefined

var foo = 'foo';
function bar() {
  console.log(foo);
};

하지만 ES2015에서 추가된 const와 let은 마치 호이스팅이 되지 않는 것 처럼 보입니다.

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

const foo = 'foo';

const와 let 변수의 호이스팅은 아래쪽에서 살펴보겠습니다.

1. 실행컨텍스트에서 호이스팅 할때의 환경

실행컨텍스트의 생성단계에서 실행컨텍스트는 다음과 같은 2가지 환경을 가지게 됩니다.
1. Lexical Environment
2. Variable Environment

1-1. Lexical Environment

Lexcical Environment는 식별자와 변수를 맵핑해주는 환경입니다. 이 환경은 3가지를 포함하는 구조를 가지고 있는데, Environment Record(환경에 대한 기록), Refrence to the outer Environment(참조된 외부환경), this binding(this 바인딩)을 포함합니다.

Environment Record는 2가지 유형이 존재합니다. 첫 번째로는 Declarative Record(선언적 환경 레코드)로 변수나 함수의 선언에 대한 환경정보를 담고 있으며, 두 번째로는 Object Record(객체 환경 레코드)로 변수와 함수의 선언을 포함하는 전역 바인딩에 대한 환경정보를 담고 있습니다. 주로 전역 컨텍스트의 환경이며 웹 환경에서는 Window 객체를 포함하는 정보를 담고 있습니다.

Lexical Environment와 Variable Environment의 다른점은 Lexical Environment 는 const, let 변수의 환경을 담당하며 Variable Environment는 var 변수의 환경을 담당합니다.

2. 호이스팅의 순서

다시 호이스팅으로 돌아와서 호이스팅의 순서는 변수선언 -> 함수선언 -> 변수할당 순서입니다.

console.log(typeof foo);  // function

var foo = 'a';
function foo() {
  console.log('function');
}

console.log(typeof foo);  // string

위의 코드를 확인하면 같은 이름을 가진 변수와 함수를 선언한 것을 확인할 수 있습니다.

  1. 첫번째로는 변수 선언이 일어나기에 foo 는 변수로 선언이 됩니다.
  2. 두번째로는 함수 선언이 일어나기에 foo 는 함수로 선언이 됩니다. 첫번째와 두번째는 실행컨텍스트가 생성되는 과정에서 일어나기에 실행 컨텍스트가 모두 생성되고 첫 번째 라인이 실행될때 foo는 두번째에서 함수로 초기화된 모습을 확인할 수 있습니다.
  3. 세번째로 var foo = 'a'에서 변수가 할당됙 때문에 마지막 라인에서는 foo가 string값으로 할당된 모습을 확인할 수 있습니다.

이처럼 var 변수는 Variable Environment에서 호이스팅되는 모습을 확인할 수 있습니다. Lexical Environment 에서 const와 let이 호이스팅 되는 과정은 조금 다릅니다.

3. const와 let의 호이스팅

아래 코드를 좀 전에 살펴본 Lexical Environmnet를 객체 형식으로 나타내어 확인해 보겠습니다.

const first = 'a';
let second = 'b';

function fn() {
  console.log(first + second);
}
  1. 실행 컨택스트 생성(Lexical Environment 생성)
{
  GlobalExectionContext = { 
    LexicalEnvironment : { 
      EnvironmentRecord : { 
        Type : "Object", 
        first: < uninitialized >,
        second: < uninitialized >,
        fn: < func >
      } 
      Outer : <null>, 
      this : <global object> 
    } 
  }
}

생성 단계에서는 모든 변수와 함수가 호이스팅 되어 선언되지만, var변수의 경우 undefined로 초기화 되는 반면 const 와 let은 초기화 되지 않은 상태로 선언됩니다.
이 때 선언만 되고 초기화되지 않은 상태를 Temporal Dead Zone(TDZ)상태라고 하며, Lexical Environmnet의 변수들이 실제로 초기화 되기 전 까지 엑세스 할 수 없는 상태를 말합니다.

  1. 코드 실행단계
{
  GlobalExectionContext = { 
    LexicalEnvironment : { 
      EnvironmentRecord : { 
        Type : "Object", 
        first: a,
        second: b,
        fn: < func >
      } 
      Outer : <null>, 
      this : <global object> 
    } 
  }
}

이어서 해당 코드들이 실행이되면서 해당 변수가 실행이 될때 값이 초기화되며 실행되게 됩니다.

let과 const가 초기화 되는 방법도 살짝 다른데, 초기화는 변수를 기준으로 우변의 코드가 먼저 실행된 후 해당 코드가 반환하는 값으로 초기화 되며 let의 경우 초기화 값이 없으면 undefined로 초기화 되며 const 경우 초기화 값이 없으면 ReferenceError 에러가 발생합니다.

let first;    // undefined
const second;   // ReferenceError

4. 함수 표현식과 함수 선언식의 호이스팅

먼저 함수는 일급 객체이기 때문에 변수에 할당할 수 있습니다.
아래와 같이 변수에 할당하는 함수를 함수 표현식이라고 합니다.
아래의 3가지 유형은 모두 함수 표현식입니다.

// 기명 함수 표현식
var multiply = function multiplyFunction(a, b) {
  return a * b;
}
// 익명 함수 표현식
var multiply = function(a, b) {
  return a * b;
}
// 화살표 함수 표현식
var multiply = (a, b) => {
  return a * b;
}

4-1 함수 선언문 호이스팅

console.log(multiply(4, 5)); // 20

function multiply(a, b) {
  return a * b;
}

함수 선언문은 실행 컨텍스트 내에서 호이스팅 단계에 선언과 초기화 할당이 동시에 일어납니다. 따라서 함수 선언문으로 선언된 함수는 해당 실행컨텍스트의 어디서나 접근이 가능한 함수가 됩니다.

4-2 함수 표현식 호이스팅

console.log(multiply(4, 5)); // ReferenceError: multiply is not defined

const multifly = function(a, b) {
  return a * b;
}
console.log(multiply(4, 5)); // TypeError: multiply is not a function

var multifly = function(a, b) {
  return a * b;
}

함수 표현식의 경우 함수 호이스팅이 아닌 변수 호이스팅이 일어나게 됩니다. 변수 호이스팅에서 살펴본 것 처럼 const로 선언한 변수는 ReferenceError가 발생하며, var로 선언한 변수는 undefined로 초기화 되여 실제로 해당 변수(multifly)의 값은 해당 라인이 실행될때 할당되기 때문에 TypeError가 발생하게 됩니다.

참고자료: https://medium.com/korbit-engineering/let%EA%B3%BC-const%EB%8A%94-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-%EB%90%A0%EA%B9%8C-72fcf2fac365

profile
🇰🇷🧑‍💻

0개의 댓글