9. 호이스팅

9-1. 호이스팅이란?

9-1-1. 정의와 개념

함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위(scope)의 최상단에 선언하는 것 같이 동작하는 현상을 말한다. 자바스크립트 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 구분하는 과정(실행컨텍스트를 위한 과정)을 거친다. 실행 컨텍스트를 위한 과정에서 모든 선언(var, let, const, function, class)을 스코프에 등록한다. 코드 실행 전 이미 변수(var)와 함수선언이 저장되어 있기 때문에 참조/호출이 먼저 나와도 오류 없이 동작한다. 호이스팅이라는 용어를 자바스크립트 실행 컨텍스트에 의한 위에 설명한 현상을 호이스팅이라고 부른다는 것으로 이해하면 되겠다. 그 현상이란 선언이 코드 실행보다 먼저 메모리에 저장되는 과정으로 인한 현상을 말한다.

9-1-2. 호이스팅이 발생하는 이유

var a = 2; 는 하나의 구문처럼 보이지만, 자바스크립트 엔진은 그렇게 보지 않는다. 'var a'‘a = 2'라는 별개의 구문으로 보고, 첫 번째는 컴파일러 단계에서 처리하고, 두 번째 구문은 실행 단계에서 처리한다. 이 의미는 스코프의 모든 선언문은 실행 전에 먼저 처리된다는 점이다.
💡
호이스팅이 발생하는 이유에 대해 더 자세히 알아보자.
 
실행 컨텍스트의 렉시컬 환경(Lexical Environment)은 환경 레코드(Environment Record)와 외부 렉시컬 환경 참조 컴포넌트(Outer LexicalEnvironment Reference)로 구성된다.
Environment Record 는 유효 범위 안의 식별자와 결과값을 바인드 해서 기록하는 영역이다. 여기에 함수 선언, 변수명 등이 담기는데, 컨텍스트 내부 전체를 처음부터 끝까지 훑으면서 순서대로 수집한다. 따라서 자바스크립트 엔진은 코드가 실행되기 전에도 이미 해당 환경에 속한 코드 변수명들을 알고 있기 때문에 호이스팅이 일어나는 것이다.

9-2. 변수 호이스팅

9-2-1. var 키워드와 호이스팅

변수(var)선언이 호이스팅으로 인해 선언이 끌어올려져서 오류가 나지 않는다.
console.log(text); // 선언, 초기화 ok text = 'Hanamon!'; // 선언, 초기화, 할당 ok var text;

9-2-2. let과 const 키워드와 호이스팅

아래 코드에서 호이스팅 때문에 선언이 끌어올려졌지만, 초기화 안 된 상태에서 참조해서 오류가 난다.
console.log(text); // 선언 ok / 초기화 x //(메모리 공간 확보와 undefined로 초기화) 참조 불가능 -> 에러 발생 let text; // 여기서 초기화 단계가 실행된다.
const text; // 애초에 const 키워드로 재할당 불가능! 그래서 선언과 동시에 할당해야 함
💡
TDZ(Temporal Dead Zone)
TDZ는 일시적인 사각지대로, 변수를 사용하는 것을 비허용 하는 개념상의 공간이다.
TDZ에 있는 값에 접근하게 되면 ReferenceError가 발생한다
정확히는 const, let이 호이스팅이 발생하지 않는 것이 아니다. 선언문을 통해 모든 식별자(변수, 함수, 클래스 등)는 호이스팅된다.
그러나 var과 다르게 초기화되기 전까지 TDZ에 머물러있기 때문에 호이스팅이 발생하지 않는 것처럼 보이며 참조가 불가능하다.

9-3. 함수 호이스팅

9-3-1. 함수 선언식과 함수 표현식의 차이

함수 선언식은 함수 이름과 함께 선언되어 호이스팅 되며, 어디서든 호출할 수 있다. 함수 표현식은 변수에 할당된 함수로, 변수 호이스팅이 발생하므로 변수가 선언되기 전에는 호출할 수 없다.
// 함수 표현식 function getName() { console.log('name'); } // 함수 선언문 var name = function() { console.log('name'); };

9-3-2. 함수 선언식의 호이스팅

함수 선언식은 전체가 호이스팅 되기 때문에 함수를 선언하기 전에도 호출할 수 있다.

9-3-3. 함수 표현식의 호이스팅

함수 표현식은 변수가 호이스팅 되지만 함수 자체는 호이스팅 되지 않으므로 변수가 선언되기 전에 함수를 호출하면 오류가 발생한다.
count(); var count = function() { console.log('count는 1이다.'); }
위 코드는 count()호출 후, var count를 선언하며 함수를 담았다. var는 호이스팅의 영향을 받으므로 위로 끌어올려지고 var count; 가 가장 먼저 실행된다. 그 후로 count()가 호출되면 위에 선언한 count가 호출되므로 변수를 호출하는 격이 된다.
var count; // undefined count(); // count는 함수가 아닌데 왜 함수를 호출하지? -> typeError var count = function() { console.log('count는 1이다.'); }
만약 var대신 let 또는 const를 썼다면 Type Error가 아닌 Reference Error가 발생할 것이다.(호이스팅의 영향을 받지 않기 때문이다.)

9-4. 클래스의 호이스팅

9-4-1. 클래스의 호이스팅

클래스는 var키워드로 선언한 변수처럼 호이스팅 되지 않고 let, const키워드로 선언한 변수처럼 호이스팅 된다. 따라서 클래스 선언문 이전에 일시적 사각지대(Temporal Dead Zone: TDZ)에 빠지기 때문에 호이스팅이 발생하지 않는 것처럼 동작한다
const Foo = ''; { // 호이스팅이 발생하지 않는다면 ''가 출력되어야 한다. console.log(Foo); // ReferenceError: Cannot access 'Foo' before initialization class Foo {} }
정리하면 호이스팅은 var, let, const, function, class키워드를 사용한 모든 선언문에 적용된다. 다시 말해, 선언문을 통해 모든 식별자(변수, 함수, 클래스 등)는 호이스팅 된다. 모든 선언문은 런타임 이전에 먼저 실행되기 때문이다.