자바스크립트에서 호이스팅(Hoisting)이란 변수 또는 함수의 선언이 맨 위로 끌어올려지는 현상을 말한다. 그래서 개발자가 어느 라인 어느 위치에 코드를 선언하더라도 실행되기 전 해당 코드가 최상단으로 끌어올려지고 실행된다.
이러한 호이스팅이 발생하는 원인은 자바스크립트의 변수 생성(instantiation)과 초기화(initialization)의 작업이 분리되어 진행되기 때문이다.
console.log(a()); // 'a'
console.log(b()); // Uncaught TypeError: b is not a function
console.log(c()); // Uncaught TypeError: b is not a function
function a() {
return 'a';
}
var b = function bb() {
return 'bb';
}
var c = function() {
return 'c';
}
따라서 위 코드에서 예상대로라면 a()부터 실행 오류가 발생할 것 같지만 a는 출력이 된다(b와 c는 오류). 왜냐하면 호이스팅에 의해 실제 자바스크립트의 컴파일은 아래와 같은 방식으로 이루어지기 때문이다.
function a() {
return 'a';
}
var b;
var c; // 함수 선언은 위로 강제로 끌어져 올라옴(호이스팅)
console.log(a());
console.log(b());
console.log(c());
b = function bb() {
return 'bb';
}
c = function() {
return 'c';
}
위에 보이는 바와 같이 함수 선언과 변수 선언이 위로 호이스팅되어 정의가 되었기 때문에 console.log(a());는 정상적으로 실행될 수 있었던 것이다. 반면 b()와 c()는 아직 변수 b와 c 안에 담기지 않은 상황이기 때문에 함수 선언문이 아니라 b와 c는 그냥 변수로 취급되어 실행 오류가 발생하는 것이다.
함수의 호이스팅
자바스크립트에서 함수를 정의하는 문법으로는 함수 표현식과 함수 선언식 두 가지가 있다.
// 함수 선언식
function add(x, y) {
return x + y;
}
// 함수 표현식
const add = function(x, y) {
return x + y;
};
이 둘은 결국 함수를 만드는 데 기본적으로 동일한 기능을 수행하지만 함수 표현식은 함수를 변수에 할당하므로 유연성이 높다. 그리고 앞선 예시에서 살펴본 바와 같이 호이스팅이 강제적으로 적용되지 않기 때문에 개발자로 하여금 혼동을 주지 않도록 한다.
이에 대한 이해를 돕기 위해 먼저 함수 선언식의 사용 예제를 보자.
console.log(add(2, 3)); // 5
function add(x, y) {
return x + y;
}
console.log(add(3, 4)); // 7
첫 번째 라인의 add(2, 3)은 아직 add()라는 함수가 정의되지 않았음에도 불구하고 add() 함수를 호출하는 것이 가능하다. 왜냐하면 코드 해석 단계에서 호이스팅이 일어나 function add()가 상단으로 끌어올려졌기 때문이다.
그러나 이러한 구조는 함수를 선언하기 전에 함수를 실행한 것이기 때문에 코드의 구조를 엉성하게 만들 수도 있다. 따라서 함수 표현식을 사용하라고 권장하는 것이다.
console.log(add(2, 3)); // Uncaught TypeError: add is not a function
// 함수 표현식 형태로 add() 함수 정의
var add = function (x, y) {
return x + y;
}
console.log(add(3, 4)); // 7
이와 같이 함수 표현식을 사용할 경우 처음의 add(2, 3)은 실행되지 않는다. 호이스팅이 일어나지 않기 때문이다.
cf) 함수 선언식과 함수 표현식 중 어느 것이 더 나은가에 대해서는 개발자들 간에 의견이 분분하다. 함수 자체는 코드 어느 라인에 선언하든 함수이기 때문에 그렇게 큰 문제가 되지 않기 때문.
변수의 호이스팅
함수뿐 아니라 변수에도 호이스팅은 일어난다. 단, 변수를 선언할 때 var 키워드를 사용했을 때의 이야기다.
var globalNum = 10; // globalNum을 전역 변수로 선언.
function printNum() {
console.log(globalNum);
var globalNum = 20; // globalNum을 지역 변수로 선언.
console.log(globalNum);
}
printNum();
/*
결과
undefined
20
*/
위 예시에서는 처음에 전역 변수로 globalNum을 먼저 선언했음에도 불구하고 첫 번째 변수 호출 값은 undefined이다. 그 이유는 자바스크립트 내부에서 함수 호이스팅에 의해 다음과 같이 코드가 변경되어 처리되기 때문이다.
var globalNum = 10;
function printNum() {
var globalNum; // 함수 호이스팅에 의해 변수의 선언 부분이 함수의 맨 처음 부분으로 이동됨.
console.log(globalNum);
globalNum = 20;
console.log(globalNum);
}
printNum();
이러한 특징으로 인해 코드의 가독성을 해치거나 예상치 못한 동작을 일으키기도 하므로 변수를 선언할 때에는 var 키워드는 금하고 ES6의 let이나 const 키워드를 사용하는 것을 강력히 권장한다. let, const로 변수를 선언하면 블록 스코프를 가지게 되므로 호이스팅이 발생하지 않기 때문이다.
'Javascript > Javascript' 카테고리의 다른 글
[Javascript] for ... of (0) | 2024.09.27 |
---|---|
[Javascript] computed property 문법(객체의 key를 변수로 접근하기) (1) | 2024.09.26 |
[Javascript] Cookie 다루기 (0) | 2024.09.24 |
[Javascript] 객체의 value 값으로 key 값 찾기 (0) | 2024.09.23 |
[Javascript] forEach()에 break 걸기 (0) | 2024.09.20 |