호이스팅은 왜 생기는 걸까?

IT공부중·2021년 2월 12일
4

JavsScript

목록 보기
19/22

호이스팅은 보통 변수를 위로 끌어올리는 것이라는 표현을 많이한다. 그렇다면 개발자들을 헷갈리게 하는 호이스팅을 왜 만들었을까?? 이를 알기 위해서는 JS엔진이 JS를 어떻게 해석하고, 어떻게 Excution Context를 만들고 어떤 작업을 하는지를 알아야한다.

JS엔진는 코드를 토크나이저/렉싱 하고 파서가 파싱해서 AST 형태로 만들고 인터프리터가 바이트코드로 만들고 실행한다. 자주 사용되는 코드는 최적화기가 최적화한다. (모든 브라우저의 JS 엔진이 동일 한건 아니다. V8기준)

JS의 스코프는 컴파일 중에 결정된다. 소스코드를 토큰으로 쪼개서 의미를 부여하는 렉싱(Lexing) 단계에 스코프가 확정되기 때문이다. 그래서 lexical scope라고 부른다. Excution Context를 만들고 Lexical environment, Variable Environment에 변수들을 기록해놓는다. 이 때가 Create Phase이다.

즉, 호이스팅은 JS 인터프리터 구현의 결과이다. 자바스크립트 엔진은 코드를 인터프리팅 하기 전에 그 코드를 먼저 컴파일 한다. var a = 2; 라는 구문을 js는 두개의 구문으로 분리하여 본다.

파싱/컴파일 단계와 실행단계로 나뉜다.

  1. var a;
  2. a = 2;

변수 선언 단계와 초기화 단계를 나누고, 선언 단계에서는 그 선언이 소스코드의 어디에 위치하든 컴파일 단계에서 처리해버린다. 여기서 이미 변수들을 메모리에 두기 때문에 초기화 전에도 접근할 수 있는 것이다. 이런 선언 단계가 호이스팅 되는 작업이라고 볼 수 있다.

다음과 같은 코드가 있다고 해보자.

var a = 3;
let b = 4;

그러면 js엔진은 먼저 코드를 컴파일하는 과정을 거치며 다음과 같은 EC를 만들어낼 것이다.

GEC {
	ThisBinding: window,
	LexicalEnvironment: {
		EnvironmentRecord: {
			b: <uninitialized>,
		},
		outer 참조: null
	},
	VariableEnvironment: {	
		EnvironmentRecord: {
			a: undefined,
		},
		outer 참조: null
	}
}

그 후에 코드를 실행한다.

실행 단계(Excution Phase)는 다음과 같이 된다고 볼 수 있다.

GEC {
	ThisBinding: window,
	LexicalEnvironment: {
		EnvironmentRecord: {
			b: 4,
		},
		outer 참조: null
	},
	VariableEnvironment: {
		EnvironmentRecord: {
			a: 3,
		},
		outer 참조: null
	}
}

변수나 함수는 이미 컴파일 과정에서 lexical envrionment나 variableEnvironment에 미리 만들어두고 쓰기 때문에 선언을 나중에 하더라도 불러다 쓰는 것이 가능하다. 하지만 값을 넣는 할당 과정은 코드가 실행이 되어야 알 수 있기 때문에 값을 미리 불러다 쓸 수는 없다. 이게 호이스팅이다. 이걸 안 되게 막으려면 추가적인 작업을 해줘야하는데, 안 하고 놔두면 그게 호이스팅이다.

let, const 호이스팅이 안 되는거처럼 보이지만 된다. 기능을 하나 추가한 것 뿐이다. 똑같이 호이스팅 돼서 EC에 등록된다. 하지만 코드를 선언하는 구문에 도달하기 전까지는 사용할 수 없도록 하는 기능이 추가 된 것이다. 아직 uninitialized로 초기화가 되어 있지 않기 때문에 사용할 수 없다. 이러한 사용할 수 없는 부분을 TDZ(temporal dead zone)이라고 부른다. var 는 undefined로 초기화를 한다. 그렇기 때문에 사용할 수 있다.

참고
에반문 블로그
문태훈님 유튜브
토스트 밋업
그 외 여러가지 블로그 및 스택오버플로우

profile
3년차 프론트엔드 개발자 문건우입니다.

0개의 댓글