[호이스팅] TDZ, var, const, let, window 바인딩 여부

호이스팅(hoisting)
인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다. var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화한다. 반면 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않는다. 따라서 var로 선언할 시 초기화 값, undefined가 나오는 것과 다르게, const와 let은 초기화 값이 없기 때문에 오류를 출력한다.
Temporal Dead Zone
변수가 현재 스코프 내에서 선언됐지만, 아직 초기화되지 않은 상태를 말한다. let 또는 const 키워드를 사용하여 변수를 선언했을 때 나오는 개념. TDZ는 변수가 선언된 지점부터 초기화되기 전까지, 해당 변수를 사용할 수 없게 한다.
TDZ에 빠진 예시
age = 20;
// Uncaught ReferenceError: Cannot access 'age' before initialization
let age;
그럼 let과 const는 호이스팅되지 않는 것인가?
let이 호이스팅되지 않는 "것처럼" 보이는 예시
console.log(name);
//ReferenceError: name is not defined
const name = 'junha';
name 선언 이전에 호출시 reference error가 나서 let은 호이스팅이 되지 않는다고 생각할 수 있다. 하지만 호이스팅 되지 않아서가 아니라 let의 변수 생성과정이 var와 다르기 때문이다.
var, let, const의 변수 생성과정 비교
(기본적으로 변수는 선언-초기화-할당의 3단계 생성과정을 가짐)
- var : 선언&초기화 동시에 이뤄짐. (선언부 & 초기화 세트로 다님)
- let : 선언 - 초기화 분리되어 진행 (선언부만 단독으로 호이스팅됨)
- const : 선언&초기화 동시에 해야 함. (선언부 & 초기화 세트로 정의해줘야 하지만 세트로 호이스팅 되진 않음)
⇒ let으로 선언한 name은 맨 위로 선언부가 호이스팅이 되긴 됐으나, 초기화(메모리 할당)가 아직 되지 않았기 때문에 reference error가 나게 되는 것이다.
age = 4;
let age;
// Cannot access 'age' before initialization
const와 let은 동일 변수를 선언하면 오류를 날린다.
var a = 0
const b = 1;
let c = 2;
console.log(a, b); // 1,2
var a = 3
const b = 4;
let c = 5;
// Uncaught SyntaxError: Identifier 'a' has already been declared
// Uncaught SyntaxError: Identifier 'b' has already been declared
Block Scope
var은 함수 스코프를 가지며, 블록 내에서 선언해도 함수 외부에서 접근 가능하며 변수 재할당이 가능하다.
const는 블록 스코프를 가지며, 블록 외부에서 접근할 수 없다.
{
var age = 20;
const name = 'junha';
}
console.log(age);
// 20
console.log(junha);
// Uncaught ReferenceError: junha is not defined
const와 let은 window에 바인딩 되지 않는다.

var로 선언한 변수는 window 객체의 프로퍼티에 저장된다. ES5까지는 사용자가 임의로 만든 변수가 모조리 window로 바인딩 되기 때문에, window가 오염이 되는 경우가 많았다. ES6부터는 let, const 및 클래스 식별자 바인딩은 window 객체에 바인딩 되지 않기 때문에, 더이상 window의 프로퍼티로 접근할 수가 없다. 전역 객체가 아니라 *별도의 선언적 환경 레코드에 보관된다. (크롬 디버거는 Script라는 이름으로 스코프를 분류하고 있다.)
* 전역 실행컨텍스트의 Lexical environment object의 environment record(=Variable Object)