JS

자바스크립트(JavaScript) - 호이스팅(Hoisting)

티베트 모래여우 2020. 11. 12. 23:54
반응형

호이스팅(Hoisting)이란?

영어사전을 찾아보면 호이스팅이 다음과 같은 뜻으로 정의되어있습니다.

Hoisting : 끌어 올리기, 들어올려 나르기

네이버 영어사전

자바스크립트에서의 호이스팅도 사전적 의미를 토대로 이해할 수 있습니다.

호이스팅은 선언된 변수, 함수의 선언부를 스코프에 따라 최상단으로 끌어올리는 개념입니다.

스코프가 전역이라면 해당 스크립트의 최상단으로 끌어올려지고 스코프가 함수 내부에 국한된다면 해당 함수의 최상단으로 끌어올려집니다.

간혹 선언과 할당 모두를 끌어올리는 개념으로 알고 계신 분도 계신데 선언부만 끌어올리는 것입니다. 할당은 이루어지지 않습니다.

또한 호이스팅은 실제 메모리에 영향을 주지 않습니다.

호잇~스팅(ㅈㅅ)


살펴보기

여기 아주 간단한 스크립트가 있습니다.

console.log(num); var num = 1;

이 스크립트를 실행하면 어떻게 될까요?

num변수를 선언하지도 않았는데 먼저 사용하고있으니 다른 언어였다면 당연히 에러가 나야 정상인 상황입니다.

하지만 실제로 스크립트를 실행하면 undefined를 출력합니다.

이런 기이한 현상을 일으킨 주범이 바로 호이스팅입니다.

즉, 상술한대로 선언부를 최상단으로 끌어올렸기 때문에 '선언만 되고 값은 할당되지 않은' 상태에서 호출을 하게 되어 undefined를 출력한 것입니다.

위에서 봤던 코드는 내부적으로 다음과 같이 동작합니다.

var num;
console.log(num);  //undefined
var num = 1;

스코프를 전역에서 함수 내부로 바꿔보아도 결과는 동일합니다.

function test(){
    console.log(num); // undefined
    var num = 1;
    console.log(num); // 1
}
test();

 


호이스팅 적용 대상

호이스팅은 ​변수/함수에 공통적으로 적용되지만, 그렇다고 모든 변수/함수에 적용되지는 않습니다.

호이스팅이 적용되는 대상은 다음과 같습니다.

1. var로 선언한 변수

위에서 본 코드를 다시 가져와 보았습니다.

console.log(num);  //undefined
var num = 1;

아까 봤듯이 num변수가 호이스팅되어 undefined를 출력합니다.

1-2. let과 const로 선언한 변수의 경우

하지만 여기서 num변수를 let이나 const로 선언한다면 에러가 발생합니다.

console.log(num);  //ReferenceError 발생
let num = 1;

위 코드는 ReferenceError를 발생시킵니다.

이는 let과 const로 선언한 변수는 선언 단계와 초기화 단계가 따로 이루어지기 때문입니다.

호이스팅으로 선언부를 끌어올려도 초기화는 실제로 let을 사용한 부분에서 이루어지기 때문에 초기화가 이루어지지 않은 값에 대해 ReferenceError를 발생시키는 것 입니다.

정리하자면 let과 const가 호이스팅이 적용되지 않는 것은 아니지만, 실제로 값이 초기화 되기 전에 접근하려고 하면 ReferenceError가 발생합니다.(이 부분은 나중에 var/let/const의 차이를 다룰 때 좀 더 자세히 알아보겠습니다.)

2. 함수 선언식으로 선언한 함수

자바스크립트에서 함수를 선언하는 방법은 2가지가 있습니다.

첫 번째는 함수 선언식으로, 이는 일반적으로 사용하는 함수 선언 방식이기에 자주 보셨을겁니다.

function test(){
    console.log("HI")
}

이와 같이 function 키워드를 이용해 함수명, 인자, 내부 코드를 작성하는 방식이 함수 선언식 입니다.

두 번째 방법은 함수 표현식 입니다.

var func = function(){
    console.log("HI")
}

함수 표현식은 위의 코드처럼 변수 안에 함수를 할당하는 형태입니다.

이 두 방법 중 첫 번째 방법인 함수 선언식만이 호이스팅의 대상이 됩니다. 함수 표현식으로 선언한 함수는 호이스팅이 적용되지 않습니다.

test(); // HI 출력
function test(){
    console.log("HI"); 
}

함수 선언식을 사용했기 때문에 호이스팅이 적용되어 test함수의 선언이 최상단으로 끌어올려져서 HI를 출력하게 됩니다.

같은 코드를 함수 표현식으로 작성하면 에러가 발생합니다.

test(); //TypeError 발생
var test = function(){
    console.log("HI");
}

함수 호이스팅이 발생하면 코드가 끔찍하게 꼬일 가능성이 높기 때문에, 가급적이면 함수를 선언할 때 함수 표현식을 이용하는게 좋습니다.(호이스팅 방지 외에도 일급 객체로서의 활용, 클로저 사용 등을 위해 표현식을 사용할 수 있습니다.)

성능 확실하구만.

 


호이스팅의 우선순위

호이스팅은 변수의 선언이 함수의 선언보다 위로 끌어올려지게 됩니다. 그리고 변수에 값을 할당했다면 그 다음에 할당이 이루어지게 됩니다.

var num1 = 1;
var num2 = 2;

function num1(){
    console.log("num1");
}
function num2(){
    console.log("num2");
}


console.log(typeof num1); //number
console.log(typeof num2); //number

위 코드는 num1, num2 변수를 먼저 선언하고 할당했음에도 불구하고 뒤에 나오는 function으로 재할당되지 않았습니다.(type이 number로 출력되는 것을 보실 수 있습니다.)

이는 위의 코드가 내부적으로 다음과 같이 호이스팅되기 때문입니다.

var num1;
var num2;
function num1(){
    console.log("num1");
}
function num2(){
    console.log("num2");
}
num1 = 1;
num2 = 2;

console.log(typeof num1); //number
console.log(typeof num2); //number

즉 num1, num2 변수를 먼저 맨 위로 끌어올려서 선언하고

그 다음 함수 num1과 num2를 끌어올려서 덮어 씌워 선언한 다음

다시 거기에 1과 2를 할당하는 과정을 거치기 때문에 최종적으로 number 자료형을 가지게 되는 것 입니다.


결론

호이스팅은 스코프를 꼬이게 만들고 코드의 가독성과 유지보수에 해로우므로 웬만하면 방지하는게 좋습니다.

따라서 let/const, 함수의 경우는 함수 표현식을 활용하고 함수와 변수를 코드 최상단에서 선언하는 습관을 들이는 것이 좋습니다.

태워버려!

반응형