호이스팅은 자바스크립트를 막 접했던 시절 가장 혼란스러웠던 개념 중 하나였다.
도대체 자바스크립트는 왜이렇게 요상한 특징을 갖고 있는건지 도무지 이해가 되지 않았었는데, 점점 깊게 공부를 해나가니 조금씩 호이스팅이 친숙해지고 있다.
이참에 호이스팅에 대해서 제대로 씹고 뜯고 맛보고 즐겨보자!
자바스크립트에서 변수 또는 함수가 코드의 최상단으로 끌어올려지는 현상.
console.log(sum(2, 3));
console.log(city);
console.log(name);
function sum(x, y) {
return x + y;
}
const name = 'Lydia Hallie';
let profile = {
age: 21,
nationality: 'Dutch'
}
var city = 'San Francisco';
1) 자바스크립트 엔진이 코드를 평가하는 과정에서 메모리에 함수의 참조값이 저장됨.
2) let
과 const
키워드로 선언된 변수들은 메모리에 uninitialized 상태로 저장됨.
3) var
키워드로 선언된 변수들은 메모리에 undefined 값으로 저장됨.
4) 코드를 평가하면서 이미 메모리에 함수의 참조값을 저장했기 때문에 코드의 실행 단계에서 함수를 선언하기 전에 호출할 수 있음.
5) var
키워드로 선언한 변수를 선언문 전에 참조하면 그 기본값은 undefined임.
6) uninitialized 상태의 변수를 선언문 이전에 참조하면 ReferenceError가 발생함. ⇒ Temporal Dead Zone
7) 자바스크립트 엔진이 코드를 순차적으로 읽으면서 앞서 선언된 변수를 만나면 메모리에 저장된 값을 실질적으로 할당된 값으로 덮어씀.
environmentRecord는 컨텍스트 내부 전체를 훑으면서 식별자들을 순서대로 수집함.
즉, 자바스크립트 엔진은 코드가 실행되기 전에 이미 해당 환경에 있는 식별자들을 알고 있음.
⇒ "식별자들을 최상단으로 끌어올려놓은 다음 코드를 실행한다"고 이해할 수 있음.
environmentRecord에는 매개변수, 변수명, 함수 선언 등의 정보가 저장됨.
environmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 집중하고, 각 식별자에 할당될 값에는 관심 없음! ⇒ 변수가 호이스팅될 때 할당 과정은 원래 자리에 그대로 남아있음.
변수는 선언부와 할당부를 나누어 호이스팅되는 것과 달리, 함수 선언은 함수 전체가 호이스팅됨.
호이스팅에 의하면 매개변수는 함수 내부의 다른 코드보다 먼저 선언 및 할당이 이루어진다고 간주할 수 있음.
호이스팅이 끝난 상태에서의 함수 선언문은 함수명으로 선언한 변수에 함수를 할당한 것으로 간주할 수 있음.
변수 선언문이 코드의 최상단으로 끌어올려진 것처럼 동작하는 자바스크립트 고유의 특징을 '변수 호이스팅'이라고 함.
자바스크립트 엔진은 변수 선언이 소스코드의 어디에 있든지 상관없이 다른 코드보다 실행되므로 어디서든지 변수를 참조할 수 있음.
변수 선언은 소스코드가 한 줄씩 순차적으로 실행되는 시점인 런타임(runtime)이 아니라 '그 이전 단계'에서 먼저 실행됨!
console.log(num); // 변수 호이스팅으로 인해 undefined 출력
var num; // 변수 선언
num = 6; // 변수 초기화
console.log(num); // ReferenceError
num = 6; // 변수 초기화
💡 변수 선언뿐만 아니라
var
,let
,const
,function
,function*
,class
키워드를 사용하여 선언하는 모든 식별자는 호이스팅됨.
함수 선언문이 코드의 최상단으로 끌어올려진 것처럼 동작하는 자바스크립트 고유의 특징을 '함수 호이스팅'이라고 함.
function catName(name) {
console.log("제 고양이의 이름은 " + name + "입니다");
}
catName("호랑이");
// 출력: "제 고양이의 이름은 호랑이입니다"
// ------------------------------------------------------
catName("클로이"); // 함수 호이스팅에 의해 catName 함수 호출 가능
function catName(name) {
console.log("제 고양이의 이름은 " + name + "입니다");
}
// 출력: "제 고양이의 이름은 클로이입니다"
var
키워드로 선언한 변수는 'undefined'로 초기화되고, 함수 선언문을 통해 암묵적으로 생성되는 식별자는 '함수 객체'로 초기화됨.
function 정의부만 존재하고 별도의 할당 명령이 없음.
function sum (a, b) {
return a + b;
}
function 정의부를 별도의 변수에 할당함.
// 기명 함수 표현식
var x = function sum (a, b) {
return a + b;
}
// 익명 함수 표현식
var y = function (a, b) {
return a + b;
}
함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점은 서로 다름!
함수 선언문은 전체가 호이스팅되는 반면 함수 표현식은 변수 선언부만 호이스팅됨.
console.log(sum(1, 2)); // 3 출력
console.log(multiply(3, 4)); // TypeError
// 함수 선언문
function sum (a, b) {
return a + b;
}
// 함수 표현식
var multiply = function (a, b) {
return a * b;
}
함수 표현식은 함수 리터럴을 변수에 할당한 것으로 변수를 선언한 것과 동일하게 동작함. ⇒ 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아닌, '변수 호이스팅'이 발생!
함수 표현식 이전에 함수를 참조하면 변수 호이스팅에 의해 undefined로 초기화되기 때문에 이때 함수를 호출하면 undefined를 호출하는 것과 마찬가지이므로 TypeError가 발생함.
💡 함수 호이스팅은 "함수를 호출하기 전에 반드시 함수를 선언해야 한다"는 규칙을 무시하기 때문에 함수 선언문 대신 '함수 표현식'을 권장함!
[도서] 정재남, ⌜코어 자바스크립트⌟, 위키북스, 2019
[도서] 이웅모, ⌜모던 자바스크립트 Deep Dive⌟, 위키북스, 2020
[MDN] 호이스팅 - 용어 사전 | MDN
[사이트] 오래된 'var'
[블로그] 🔥🕺🏼 JavaScript Visualized: Hoisting