JavaScript Variable Environment: Hoisting and The TDZ

모든 출처는 에서 가져왔습니다.  

https://tecoble.techcourse.co.kr/post/2021-04-25-hoisting/


호이스팅(Hoisting) 이란?


변수, 함수의 선언부가 위치한 인접 스코프의 시작 지점에서 해당 식별자가 관측 가능한 현상이다.


1
2
3
4
5
Hoisting(); //hoisting
 
function Hoisting(){
  console.log('hoisting');
};
cs


전역 스코프에서 선언한 위치와 관계없이 선언 대상을 확인 할 수 있다.  따라서 아래 코드와 같이 어떤 기술이 선언 대상을 스코프의 최상단으로 끌어올린다고 생각 할 수 있다.


1
2
3
4
5
6
function Hoisting(){
  console.log('hoisting');
};
 
 
Hoisting();   //hoisting
cs


호이스팅의 뜻이 끌어올리다 이므로 다음과 같은 오해를 하기 쉽지만 이는 비유적 표현일 뿐이다. 자바스크립트 엔진은 실행 시점에서 특정한 변수나 함수를 상단으로 끌어올릴 만한 능력은 없다. 실행 과정에서 변수나 함수에 대한 전체적인 정보를 얻을 과정이 있었을 뿐이다.

그러한 과정에 대해 먼저 알아보겠다.


우선 실행 컨텍스트에 대해 간단히 복습해보자.

실행 컨텍스트란 실행할 코드에 제공할 환경 정보들을 모아놓은 객체이다.

실행 컨텍스트가 활성화 될때에는 세가지 일이 일어나는데

1. 호이스팅이 발생한다.

2. 외부 환경 정보를 구성한다.

3. this 값을 설정한다.


실행 컨텍스트의 구성에는

1. VariableEnvironment

현재 컨텍스트 내의 식별자(변수)들에 대한 정보

외부 환경 정보

선언 시점의 LexicalEnvironment의 스냅샷(변경 사항 저장 x)


2.LexicalEnvironment

처음에는 ViariableEnvrionment와 같지만 변경사항이 실시간으로 반영됨


3.ThisBinding

식별자가 바라봐야 할 대상 객체


VariableEnvironment의 내용은 LexicalEnvironment와 같지만, 최초 실행시의 스냅샷을 유지한다. 실행 컨텍스트를 생성할때 VariableEnvironment에 정보를 먼저 담고 이를 복사해 LexicalEnvironment를 만든다.


LexicalEnvironment의 내부에는 environmentRecord와 outerEnvironmentReference로 구성돼 있다.

environmentRecord 로 인해 호이스팅이 발생하고 outerEnvironmentReference로 인해 스코프와 스코프 체인이 형성된다.


environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장된다.

1.매개변수 식별자

2.함수 자체

3.함수 내부의 식별자

environmentRecord 에는 다시 3개의 하위 클래스를 가지는데

1.선언적 환경 레코드

선언적 환경 레코드는 var,const,let,class,module.import,function 등으로 스코프 내에서 선언된 식별자들의 바인딩을 관리한다.

2.객체 환경 레코드

3.전역 환경 레코드



앞서 변수 객체는 declartive environment record 이다.


다시 돌아와서 자바스크립트 엔진은 실행 전에 모든 스코프(or 실행 컨텍스트)를 탐색하며 ㅅ각 스코프의 변수 객체에 여러 식별자를 수집한다. 따라서 실행 전 선언된 식별자에 대한 정보를 이미 알고 있기 때문에 스코프의 어느 지점이든 관련된 함수/변수를 참조할 수 있는 것이다. 그렇지만 함수/변수에 상관없이 늘 동일한 규칙이 적용되지는 않는다.



호이스팅은 크게 변수 호이스팅과 함수 호이스팅으로 나뉜다. 그리고 다음과 같은 규칙을 따른다.


1. 선언 된 함수는 상단에서 참조,호출이 가능하다.

2. 선언된 var 는 상단에서 참조 ,할당이 가능하다.

3. 선언된 let, const 는 상단에서 참조, 할당이 불가능하다.




Function Hoisting

함수 호이스팅은 다른 무엇보다 가장 먼저 이루어 진다!! 함수 호이스팅은 선언문에만 해당되는데 이는 굳이 어렵게 받아들일 필요가 없다. 함수 표현식은 결국 어떠한 변수로 함수를 할당하는 모양이라서 이것 또한 변수 호이스팅 사례로 볼 수 있기 때문이다.


함수의 선언문은 식별자가 변수 객체에 수집될 때 부가적으로 해당 함수 참조에 대한 초기화 까지 자동으로 이루어진다. 그래서 선언된 함수는 상단에서 참조,호출이 가능하다.


Variable Hoisting

변수는 프로그램 내에서 크게 세가지 단계를 거친다.


1. 선언 : 파싱 과정에서 변수 객체가 변수에 대한 식별자들을 수집한다.


2. 초기화 : 식별자에 메모리를 할당하고 undefined 상태를 부여한다.


3. 할당 :  변수 안에 직접 값을 넘겨 준다.


함수 호이스팅 과정에서 함수의 식별자를 선언하고 참조값을 초기화 한 것처럼 변수 또한 마찬가지로 선언과 초기화를 해주어야만 값의 참조 및 할당이 가능하다. 다만 변수를 어떻게 선언했는지에 따라 선언, 초기화 시점이 달라질 수 있다는 점을 유의 해야한다.


var 은 호이스팅이 발생하면, 선언과 초기화가 거의 동시에 이루어진다. 실행 시점의 스코프 최상단에서 해당 변수에 대한 메모리가 살아있기 때문에 선언부 위치에 상관 없이 참조, 할당이 가능하다.


let, const 는 호이스팅이 발생하면 선언만 이루어지고 실행 시점에서 실질적인 선언부를 만날 때까지 초기화는 이루어지지 않는다. 이 간극 만큼 해당 변수에 대한 메모리는 존재하지 않기 때문에 선언부 상단에서 참조,할당이 불가능하다.



let, const 가 동작하는 과정에서 스코프의 진입지점과 해당 식별자의 실질적 선언부 사이를 일시적 사각지대, TDZ(Temporal Dead Zone) 라고 한다. 여기서 변수는 존재 하지만 , 초기화가 되어 있지 않다.


TDZ 에서 let, const 값을 참조하면 "Cannot access 'a' before initialization"

이란 오류가 발생한다. 이는 선언을 안한 변수를 사용하게 되면 나오는 오류

"a is not defined" 와 다르다.

이는 자바스크립트가 let 의 식별자를 관측을 했고 이를 TDZ 안에서만 초기화를 안했기 때문에 다른 에러 메세지가 발생하는 것이다.

호이스팅은 인접 스코프의 상단에서 선언부를 관측할 수 있는 현상 이라는 설명에 따르면 let, const 또한 호이스팅 대상이라고 볼 수 있다.




함수 호이스팅은 수 많은 함수를 어떤 식으로 나열 할지 자율성을 갖는 장점이 있다. 이는 팀적으로 컨벤션적인 부분으로 이용가능하고 그저 나열하던지 표현식 처럼 정리하던지 자유롭게 이용하면 된다.


변수 호이스팅 함수 호이스팅에 비해 조심해야 한다. var 은 코드의 오류 발생 확률도 높일 수 있고 유연한 특성 때문에 발생하는 피해를 방지하고자 let 과 const 가 출현하게 되었다.



호이스팅은 엔진의 능력도 아니게 실행 시점에서 발생하는 현상 또한 아니다. 그저 js의 특성에 따라 컴파일을 거치며 자연스로 귀결 되는 전처리 과정이다. 






















댓글

이 블로그의 인기 게시물

Javascript this keyward

Javascript Object vs Primitives (Primitives vs reference type)

javascript 15 switch statement