console.log(score); //undefined
var score;
위 소스코드를 실행시키면 undefined
가 출력된다.
뭔가 이상하다. 자바스크립트는 컴파일러가 아니라 인터프리터로 코드를 번역하기 때문에 위에서부터 차례대로 한 줄 씩 소스 코드를 실행시킨다. 또한 선언되지 않은 변수를 참조할 경우 ReferenceError
를 출력한다. 위 코드는 변수가 생성되기 전에 변수를 참조했음에도 불구하고 에러가 발생하지 않는다.
즉, console.log(score);
가 실행될 때는 아직 score
변수가 생성되지 않았으므로 ReferenceError
가 출력되어야 정상이지만 실제로는 undefined
가 출력된다.
이제 그 이유를 알아보자.
자바스크립트는 소스코드가 실제로 실행되는 단계인 런타임 이전에 소스코드 평가 과정을 거치는데, 이 과정에서 자바스크립트 엔진은 변수의 선언을 포함한 모든 선언문을 먼저 실행시킨다. 이처럼 모든 선언문을 스코프의 가장 선두로 끌어 올린 것처럼 동작하는 자바스크립트 고유의 특징을 호이스팅(hoisting) 이라고 한다.
var 키워드로 선언한 변수가 생성되기 전에 참조해도 에러가 발생하지 않고 undefined
가 출력된다. 이는 아래 코드처럼 선언과 동시에 값을 할당해주더라도 마찬가지다.
console.log(score); // undefined
var score = 20;
console.log(score); // 20
위 코드가 실제로는 아래처럼 동작한다.
var score; // 호이스팅으로 인해 끌어올려진 선언문
console.log(score); // undefined
var = 20;
console.log(score); // 20
자바스크립트 엔진은 변수 선언을 2단계에 거쳐 수행한다.
undefined
를 할당해 초기화한다.var
키워드로 생성된 변수는 “선언 단계” 와 “초기화 단계” 가 동시에 진행된다. 따라서 var 키워드로 선언된 변수는 암묵적으로 undefined
가 할당되기 때문에 에러가 발생하지 않는다.
변수의 생성 이전에 참조가 가능하다는 점은 오류를 발생 시키지는 않지만 코드의 가독성을 나쁘게 만들 뿐더러 예상치 못한 결과를 야기할 수도 있다. 이러한 이유로 ES6에서 let
과 const
키워드가 도입되며 var
키워드의 사용은 지양되고 있다. 그렇다면 let
과 const
로 생성한 변수에서는 변수 호이스팅이 작동하지 않는 것인가?
let
과 const
를 사용하더라도 변수 호이스팅은 작동한다. 다만 호이스팅이 발생하지 않는 것"처럼" 동작한다. 아래 코드를 보면 let
키워드로 변수를 생성할 경우 변수의 선언문 이전에 변수를 참조하면 ReferenceError(참조 에러)
가 출력되는 모습을 확인할 수 있다.
console.log(score); // ReferenceError: score is not defined
let score;
console.log(score); // undefined
let 과 const 키워드로 선언한 변수는 “선언 단계”와 “초기화 단계”가 분리되어 진행된다. 그리고 두 단계의 사이에 '일시적 사각지대(Temporal Dead Zone: TDZ)' 라는 것이 생긴다.
변수가 선언 단계를 거친 뒤 이 구간에 있을 때 변수에 참조할 경우 ReferenceError
가 발생한다. 따라서 위 코드를 실행시키면 실제론 아래 코드처럼 동작한다.
// 호이스팅 발생, 선언 단계
let score;
// 일시적 사각지대(TDZ) 구간
console.log(score); // ReferenceError
// 초기화 단계
score = undefined;
// 참조
console.log(score); // undefined
따라서 score
변수가 호이스팅 되었더라도 실제로 변수 선언문을 만나 초기화 되기 전까지는 변수를 참조할 수 없다.
내용을 요약해보자.
var
키워드로 변수를 생성할 경우 선언 단계 와 초기화 단계 가 동시에 일어나기 때문에 변수의 선언문 이전에 해당 변수를 참조하면 undefined
가 출력된다.
let
과 const
로 변수를 생성할 경우 선언 단계와 초기화 단계 사이에 일시적 사각지대(TDZ) 가 있으므로 변수의 선언문 이전에 해당 변수를 참조할 경우 해당 변수는 아직 초기화가 이루어지지 않았기 때문에 ReferenceError
가 발생한다.