자바스크립트 변수 생성은 세 단계로 이루어진다.
[선언]
- 메모리에 변수를 위한 공간이 확보되고 변수가 스코프에 등록된다.
[초기화]
- 변수는 undefined로 초기화된다.
- 변수를 위한 공간을 메모리에서 할당받는다.
[할당]
- undefined로 초기화된 변수에 실제 값을 할당한다.
메모리에 공간을 확보한다는 것은 메모리 관리 시스템으로부터 일정량의 메모리를 요청하는 것이고, 메모리의 공간을 할당받는다는 것은 확보된 메모리를 변수에 할당하는 것을 말한다.
선언과 초기화가 한 번에 이루어진다.
var
키워드로 선언된 변수는 선언된 순간 undefined로 초기화된다. 따라서 선언문 이전에 변수에 접근하면 undefined를 반환한다.
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 'Hello World!';
console.log(foo); // 'Hello World!'
선언과 초기화가 분리되어 진행된다.
let
과 const
로 선언된 변수는 스코프에 등록되고, 선언문에 도달했을 때 비로소 초기화가 진행된다. 선언문 이전에 변수에 접근하면 Reference Error가 뜬다. 메모리에 변수를 위한 공간을 할당받는 초기화가 진행되지 않았기 때문이다.
console.log(foo); // Reference Error
let foo;
console.log(foo); // undefined
foo = 'Hello World!';
console.log(foo); // 'Hello World!'
이처럼 스코프에 등록된 시점부터 초기화되기 전까지의 구간을 일시적 사각지대(Temporal Dead Zone)라고 한다.
변수가 스코프에 등록된 시점부터 초기화되기 전까지의 구간으로, var 키워드에는 없고 const와 let에만 존재하는 개념
ES6 이후에 나온 const
와 let
키워드는 블록 레벨 스코프를 갖는다.
예시에서let
키워드로 스코프 안에 선언된 변수 foo는 지역 변수다. 해당 스코프의 처음부터 선언문을 만날 때까지 TDZ를 거치기 때문에 Reference Error가 발생하는 것을 알 수 있다.
let foo = 'Hello';
{
console.log(foo); // Reference Error
let foo = 'Hi'; // 지역 변수
}
같은 예시를 var
키워드로 선언하면 Reference Error가 아닌 Hello가 뜨는 것을 확인할 수 있다.
var
키워드로 선언된 변수는 블록 레벨 스코프를 따르지 않기 때문에 전역에서 접근 가능하기 때문이다.
var foo = 'Hello'; // 전역 변수
{
console.log(foo); // 'Hello'
var foo = 'Hi'; // 전역 변수
}
console.log(foo); // 'Hi'
변수와 함수의 선언이 스코프의 최상단으로 올려져 실행되는 것을 말한다.
결론적으로 var, let, const 모두 호이스팅이 발생한다. 다만 변수 초기화 여부(TDZ 존재 여부)에 따라 그 결과가 다르다.
자바스크립트 엔진은 코드를 실행하기 전에 모든 선언을 메모리에 등록한다.
이 때 var
키워드로 선언된 변수는 선언과 동시에 undefined
로 값이 초기화되기 때문에 선언문까지 도달하지 않아도 해당 변수의 값을 참조할 수 있다.
console.log(foo); // undefined
var foo;
let
과 const
키워드도 호이스팅되는 것은 마찬가지다. 그러나 값이 TDZ에 빠져 초기화가 이루어지지 않았기 때문에 Reference Error가 발생하는 것이다.
console.log(foo); // Reference Error : cannot access foo before initilaization
let foo;
변수 호이스팅을 방지하기 위해서 변수 선언은 const, let을 이용하자.
함수 호이스팅은 함수를 어떤 방식으로 정의했냐에 따라 달라진다.
함수명이 정의되어 있고, 별도의 변수에 할당하지 않은 것
함수 선언의 경우 선언, 초기화, 할당이 모두 동시에 발생한다. 따라서 함수 선언식으로 함수를 정의했을 때 함수 전체가 스코프의 최상단으로 올려져 호이스팅이 발생한다.
func1(); // 'Hi'
function func() {
console.log('Hi');
}
정의한 함수를 별도의 변수에 할당하는 것
var
로 선언된 func2의 경우 선언 되자마자 undefined로 값이 초기화되었다. 따라서 함수가 아니기 때문에 실행할 수 없다는 에러가 뜬다.
console.log(func2); // undefined
func2(); // func2 is not a function
var func2 = function () {
console.log('Bye');
}
const
로 선언된 func3의 경우 초기화도 이루어지지 않았다. 따라서 Reference Error가 뜬다.
console.log(func3); // Reference Error : cannot access foo before
const func3 = function () {
console.log('Good morning');
}
함수 호이스팅을 방지하기 위해서 const 변수에 함수를 할당해 사용하자. (let으로 해줄 필요가 없는 이유는 함수가 객체 타입이기 때문이다.)