얼마 전 프론트엔드 개발자분들이 모여 계신 커뮤니티에 호이스팅(Hoisting)에 관한 이야기가 나온 적이 있었습니다.
선언과 할당에 관련된 용어인것은 알았지만, 정확한 의미에 대해서는 몰랐기 때문에 이번 기회에 찾아보게 되었습니다.
이번 포스팅에서는 검색을 통해 알게 된 내용을 정리하고, 간단하게나마 확인해보겠습니다.
호이스팅이란?
호이스팅의 정의는 자바스크립트에서 함수나 변수 선언이 해당 스코프의 최상단으로 끌어올려지는 현상을 말합니다.
다만 실제로 끌어 올려지는 것은 아니고, 마치 그런 것처럼 보이는 현상인데요. 이게 어떤 의미인지 코드를 통해서 확인해보겠습니다.
var fruit = "apple";
function change() {
console.log("fruit is : " + fruit);
var fruit = "banana";
console.log("fruit is : " + fruit);
}
1. 위 코드에서 전역변수 fruit가 먼저 선언이 되었고, apple 이라는 string으로 할당이 되었습니다.
2. change 함수에서, console.log를 통해 fruit에 있는 값을 콘솔창에 출력합니다.
3. fruit 함수를 다시 선언하고, banana 라는 string으로 재할당을 합니다.
4. console.log를 통해 fruit에 있는 값을 콘솔창에 출력합니다.
위 과정을 보면, 2번과 4번의 console.log에선 각각 apple, banana가 출력되어야 할 것 같습니다.
그럼 실제로 실행해 본 결과를 보겠습니다.
+ 그냥 코드만 입력하면 함수 안의 내용이 실행이 안되기 때문에 마지막 줄에 change() 함수를 호출해주었습니다.
콘솔창에 출력된 내용을 보시면 예상했던 결과와는 살짝 다른 모습입니다.
두 번째 로그에선 banana가 출력되었지만, 첫 번째 로그에선 apple대신 undefined가 출력되었는데, 어떻게 된 일일까요?
이 현상을 이해하기 위해 먼저 유효 범위를 살펴보겠습니다.
유효 범위
자바스크립트의 유효 범위는 블록 단위로 나누어집니다. 이를테면 한 중괄호 { } 내부에서 선언된 변수는 이 블록 외부에서는 사용할 수 없습니다.
어떠한 함수에도 속하지 않는 변수의 경우 전역변수라고 부릅니다. 전역변수는 자바스크립트의 최상위 코드이며 어디에서도 사용할 수 있습니다. 위에서 사용했던 코드도 마찬가지입니다.
위 코드에서 처음 선언되었던 fruit는 어느 함수에도 속하지 않는, 전역변수입니다. 따라서 change 함수 내에서도 접근해서 사용할 수 있습니다. 반대로, 전역변수로 fruit가 선언되지 않았다면, change 함수 바깥에서는 지역변수인 fruit에 접근할 수 없게 됩니다.
그렇다면 fruit가 전역변수로 먼저 선언되었음에도 불구하고, 어째서 undefined가 뜨게 되었는지 알아봅시다.
호이스팅
위에서 작성했던 순서를 다시 한 번 봅시다.
1. 위 코드에서 전역변수 fruit가 먼저 선언이 되었고, apple 이라는 string으로 할당이 되었습니다.
2. change 함수에서, console.log를 통해 fruit에 있는 값을 콘솔창에 출력합니다.
...
코드는 위에서 아래로 차례대로 흘러가기 때문에, 위 순서는 일견 맞는 것처럼 보입니다.
그러나 자바스크립트의 실행 과정을 보게되면 살짝 다른것을 알 수 있습니다.
자바스크립트 엔진은 인터프리팅 직전에 컴파일레이션이라는 과정을 거쳐갑니다.
컴파일레이션중 코드 생성 과정에서 사전에 변수를 생성하고 메모리에 저장하는 동작을 하게되는데 이때 블록 안에서 호출하려던 전역변수와 같은 이름의 지역변수가 있다면, undefined를 값으로 갖는 변수로 저장됩니다.
이러한 과정을 거치기 때문에 지역변수는 유효범위의 맨 위에서 선언되는 것처럼 보이며, 할당은 원래의 코드 위치에서 이루어지기 때문에 할당 이전에 console.log를 실행하게되면 undefined만 출력하게됩니다.
이를 호이스팅이라고 부릅니다.
호이스팅의 범위
호이스팅이 모든 변수와 함수에게서 일어나지는 않습니다. 함수에서는 표현식이 아닌 선언식만이 호이스팅의 대상이고 변수에서는 var 키워드로 선언한 변수만이 호이스팅 현상이 나타나는 것처럼 보입니다. 그렇다면 let과 const는 호이스팅에서 자유로운걸까요?
var의 변수 생성 과정은 선언 및 초기화, 할당으로 구성되어 있습니다. 반면 let은 선언, 초기화, 할당 세 단계를 거칩니다.
호이스팅은 선언 부분이 상단으로 끌어올려지는 현상입니다. 따라서 var의 경우엔 선언과 초기화가 묶여있기에 상단 부분에서 초기화까지 이루어집니다. 변수는 초기화 과정에서 undefined를 부여하기 때문이죠.
그러나 let은 선언과 초기화가 분리되어 있기에, 선언 부분만 상단으로 올라가게 되면 초기화가 되지 않은 상태가 됩니다. 따라서 초기화 이전에 변수를 호출하게 되면 undefined 값이 부여되지 않은 상태이기 때문에 레퍼런스 에러를 출력하게 됩니다.
const는 애초에 선언과 할당이 분리될 수 없습니다. const로 선언된 변수는 상수이기 때문에 변경될 수 없고, 선언과 동시에 초기화가 이루어져야 합니다. 따라서 먼저 참조를 하게되면 접근할 수 없다는 구체적인 에러를 반환합니다.
함수 선언문 또한 어떤 변수를 정의하고 함수 객체를 그 변수에 할당하는 역할까지 하는 반면, 함수 표현식은 변수를 정의하지 않고 할당만 하기때문에 실제 변수에 할당되기 전까지 참조할 수 없습니다. 끌어올려지는 것(호이스팅)은 선언에만 적용된다고 생각하시면 좋을 것 같습니다.
호이스팅을 통해 자바스크립트의 내부적인 과정을 어느정도 엿볼 수 있었습니다.
엔진을 직접 뜯어보며 더 알아보면 재밌을 것 같다는 생각이 듭니다.
'이론 > Frontend' 카테고리의 다른 글
동기와 비동기 (Synchronous & Asynchronous) (0) | 2020.09.18 |
---|---|
자바스크립트의 이벤트 버블링 (JS eventBubbling) (0) | 2020.09.17 |
CSS 적용 우선순위 (0) | 2020.09.08 |
싱글 쓰레드 Javascript의 비동기적 동작 (0) | 2020.09.07 |
React에서 PC/모바일 각각 라우팅하기 (0) | 2020.07.16 |
댓글