변수를 선언하는 키워드인 var, let, const의 호이스팅과 스코프를 설명한다.
var키워드를 사용한 변수 선언은 '선언단계'와 동시에 '초기화단계'가 진행된다.
var변수는 암묵적으로 undefined
로 초기화된다.
초기화하지않으면, 변수에게 확보된 메모리공간에 다른 어플리케이션이 사용했던 값(쓰레기 값)이 남아있을 수 있다.
console.log(ex); // undefined
var ex; //변수 선언
변수선언보다 먼저 변수를 참조했음에도 불구하고 reference error
가 아닌 undefined
에러가 뜬다.
그 이유는 변수선언이 소스코드가 실행되는 런타임이 아니라, 그 이전단계에서 먼저 실행되기 때문이다.
자바스크립트는 소스코드가 실행하기 전에, 먼저 소스코드의 평가를 거쳐 소스코드를 실행하기 위한 준비를 한다. 이때 모든 선언문(변수, 함수)등을 소스코드에서 찾아내 먼저 실행한다. 그리고 평가과정이 끝나면 그때서야 모든 선언문을 제외하고 소스코드를 한줄씩 순차적으로 실행한다. (이때가 런타임이다.)
즉, 자바스크립트 엔진은 변수 선언이 소스코드의 맨 아래에 있다고해도, 다른 코드보다 먼저 변수선언을 실행한다. 이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것 처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅이라고 한다.
사실 변수 선언 뿐 아니라 모든 식별자(변수, 함수, 클래스 등)은 호이스팅된다. 앞서 말한 것 처럼 모든 선언문은 런타임 이전단계에서 먼저 실행되기 때문이다.
그렇다면 아래 코드의 실행결과는 어떻게 될까?
console.log(ex); // ??
var ex = 'hi'; //변수 선언과 값의 할당
console.log(ex); // hi
console.log(ex); // ??
ex = 'hi'; // 값의 할당
var ex; //변수 선언
console.log(ex); // ??
var는 함수레벨 스코프이다.
함수의 코드 블록만을 지역 스코프로 인정한다.
var foo = 1; // 전역 변수
if (true) {
var foo = 2; //전역 변수. 이미 선언된 전역변수 foo가 있으므로 값이 덮어씌어진다.
}
console.log(foo); // 2
let, const는 블록레벨 스코프이다.
모든 코드 블럭(if, for, while, try/catch, 함수 등)을 모두 지역 스코프로 인정한다.
let foo = 1; // 전역 변수
{
let foo = 2; //지역변수
let doo = 3; //지역변수
}
console.log(foo); //1
console.log(doo); // reference error
let으로 선언한 변수는 '선언단계'와 '초기화 단계'가 분리되어 진행된다.
런타임 이전에 암묵적으로 선언단계가 먼저 실행되지만, 초기화 단계는 변수 선언문에 도달했을 때 실행된다. 만약 초기화 단계 이전에 변수를 참조하면 참조에러가 뜬다.
이처럼 let은 변수 호이스팅이 발생하지 않는 것 처럼 동작한다.
하지만 변수 호이스팅은 동작한다.
아래의 예시에서, let 변수가 변수 호이스팅이 되지 않는다면 전역 변수인 1을 출력해야 한다.
하지만 호이스팅이 발생하기 때문에 참조 에러가 뜬다.
let foo = 1; // 전역 변수
{
console.log(foo); // 초기화 이전에 접근할 수 없다는 참조에러가 뜬다.
let foo = 2; // 지역 변수
}
const으로 선언한 변수는 반드시 선언과 동시에 초기화해야한다.
const foo; // syntax error: 초기화 안됨
let과 마찬가지로 블록 레벨 스코프를 가지며, 변수 호이스팅이 발생하지 않는 것 처럼 동작한다.
참고: 모던 자바스크립트 딥다이브
"var : 함수레벨 스코프"에서 코드상에서 var가 사용되어야하는데 let으로 작성되었습니다.
"let, const : 블록레벨 스코프"에서 오타가 있습니다.
console.log(var); // reference error
==> console.log(bar); // reference error