호이스팅은 변수와 함수를 선언하는 위치와 상관없이 스코프 최상단에 끌어올려지는 것을 의미한다.
좀 더 쉽게 설명해보자면, 변수가 어디에 적여있든지(선언된 시점) 상관없이 맨 위로 끌어와서 가장 먼저 실행하는 것을 말한다.
console.log(a); // undefined
var a = 1;
console.log(a); // 1
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
a가 console.log 앞에 선언되어 있지 않는 "호이스팅" 상태이지만, var 변수로 선언하게 되면 기본적으로 undefined로 초기화 된다.
let 역시 호이스팅이 되지만, 결과는 b를 찾을 수 없다는 참조에러(ReferenceError)가 뜬다.
에러 메시지를 자세히 보면, 초기화(initialization)전에 접근(access)할 수 없다고 나온다.
즉, 두 변수는 호이스팅으로 인해 선언되어 있지만, var는 undefined라는 오류를 나타내며
let으로 선언된 b의 변수값은 TDZ(일시적 사각지대)로 인해 아직 초기화가 되지 않아 접근이 안되는 것이다.
1. 선언 단계 (declaration phase)
2. 초기화 단계 (initialization phase)
3. 할당 단계 (assignment phase)
var 변수는 선언과 초기화가 한번에 이루어진다.
그렇게 때문에 변수 a의 값을 불러오기 위해 접근했을 때, 아직 실제 값이 "할당"되지는 않았지만 undefined라는 값을 불러올 수 있는 것이다.
반면, let과 const는 선언과 초기화 단계 사이에 TDZ(일시적 사각지대)라는 것이 존재한다.
아직 선언도 안되어 있는데 접근한다면, 심각한 에러가 발생할 수 있기 때문에 이를 막기 위해
TDZ가 있는 것이다.
// 함수 참조
console.log(sum); // sum(x, y)
// 함수 호출
console.log(sum(6, 8)); // 14
// 함수 선언
function sum(x, y) {
return x + y;
};
위 코드에서는 sum이라는 이름을 가진 함수 역시 맨 위에 선언이 되어있지 않지만, 함수를 참고하고 호출할 수 있어서 호이스팅이 가능하다.
// 함수 참조
console.log(sum); // undefined
// 함수 호출
console.log(sum(6, 8)); // TypeError: sum is not a function
// 함수 표현식 선언
var sum = function (x, y) {
return x + yield;
};
하지만, 함수 표현식으로 함수를 호출했을 경우에는 타입에러(TypeError)가 뜬다.
가장 먼저, sum을 참조하면 undefined가 나오는데 이는 변수 호이스팅에서 살펴봤듯이 var 변수에 접근하여 아직 "실제 값"이 할당되지 않았기 때문에 undefined를 출력해준다.
그리고, 함수 표현식에서 "실제 값"은 "함수"이다.
따라서, 실제 함수 값을 console.log 이전에 할당해주지 않았기 때문에 undefined라는 값을 출력하고, sum의 타입은 undefined이기 때문에 sum의 타입이 함수가 아니라는 타입에러 메시지가 뜨는 것이다.
결과적으로, 함수 표현식에서는 함수의 이름만 호이스팅이 되고, 함수 표현식 자체는 끌어올려지지
않기 때문에 함수 표현식에서는 호이스팅이 "불가능"하다.
공통점 : 런타임 이전에 실행되어 식별자를 생성한다는 점에서 동일하다.
차이점 : var 변수는 undefined로 초기화되고, 함수 식별자는 함수 객체로 초기화된다.
따라서, var 변수를 참조하면 undefined로 평가되지만 함수 선언문 이전에 함수를 호출할 수 있다.