[JavaScript] 호이스팅(Hoisting)
개 요
지금의 나는 자바스크립트를 어느정도 사용할 줄은 안다고 하지만 에러발생(특히 휴먼 에러)같은 상황에 직면 했을 때, 아직 JS의 아이덴티티에 대해서는 깊게 모르고 있다는 생각이 들었다. 때문에 JS의 아이덴티티에 대해 차근차근 포스팅해보려고 한다.
개 념
MDN문서에 나와 있는 내용을 그대로 발췌하면
'인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것'
추가적인 설명을 요약해서 덧붙이자면 '함수, 변수(var)와 같은 선언문을 유효 범위의 최상단으로 끌어올린다.' 라고 생각하면 될 것 같다. 따라서 결론적으로 이야기하자면 변수나 함수를 정의하는 코드보다 사용하는 코드가 앞서서 등장할 수 있다.
예를 들면 다음과 같다.
// 일반적인 코드 작성 순서
function printName(name) {
console.log("이름은 " + name + " 입니다."); // 이름은 정민교 입니다.
}
printName("정민교");
//----------------------------------------------------------------------
// 호이스팅
printName("정민교");
function printName(name) {
console.log("이름은 " + name + " 입니다."); // 이름은 정민교 입니다.
}
일반적인 코드 순서로는 함수를 먼저 정의하고 사용을 하지만 자바스크립트에서는 함수의 호출부분이 먼저 오고 그 뒤에 선언 부분이 있어도 정상적으로 잘 동작한다. 이게 가능한 이유가 바로 호이스팅이다.
이게 어떻게 가능할까? 먼저 아래의 코드를 살펴보자
for(var i=0; i<10; i++) {
// do
}
console.log(i); // 10
//--------------------------------------------------------
if(true) {
var test = "테스트";
}
console.log(test); // 테스트
//--------------------------------------------------------
function fnTest(name){
var firstName = '정';
console.log(firstName+name);
}
fnTest("민교"); // 정민교
변수의 선언문 쪽에 포커싱을 해보자. 일반적으로 변수의 선언문은 블록 스코프에서만 유효하다. 이러한 관점에서 봤을 때 for문의 i와 if문의 test는 해당 블록이 종료되는 시점에 없어지게 되어 출력부분에서 출력이 되지 않아야하는게 정상이지만, JS에서는 선언문을 해당 유효범위의 최상단으로 끌어올리게 되는데 이것을 호이스팅이라고 하는 것이다.
그럼 위의 코드는 결과적으로 아래와 같이 해석되어 실행된다.
// ▼ 호이스팅
var i;
var test;
function fnTest(name){
var firstName;
firstName = '정';
console.log(firstName+name);
}
// ▲ 호이스팅
for(i=0; i<10; i++) {
// do
}
console.log(i); // 10
//--------------------------------------------------------
if(true) {
test = "테스트";
}
console.log(test); // 테스트
//--------------------------------------------------------
fnTest("민교"); // 정민교
함수와 변수의 선언부가 모두 최상단으로 올라가서 해석이 되기 때문에 최상단에 있는 코드와 같은 형태가 작동이 될 수 있는 것이다.
단, 실제로 코드가 끌어올려지는 게 아니고 JS Parser 내부적으로 끌어올려서 처리를 하는 것이라 실제 메모리에는 변화가 없다.
또한 변수같은 경우 var 키워드로 선언된 변수만 호이스팅이 된다고 알려져있지만 ES6의 let과 const도 호이스팅이 일어난다.
하지만 let과 const의 경우 변수가 초기화 되기 전 까지 temporal deadzone(죽어있는 공간)에 머물게 되어있다.
아래 코드로 조금 더 이해하기 쉽게 설명을 하자면
/* let a; -> 호이스팅이 일어났지만 초기화가 되기 전까지 deadzone에 있는 상태 */
console.log(a); // 참조에러
let a = '정'; /* 초기화가 일어난 후 호이스팅된 변수는 deadzone을 벗어난 상태 */
console.log(a); // 정
변수 a는 호이스팅이 되어있지만 초기화가 되기 전 temporal deadzone에 있는 상태이기 때문에 첫 번째 console.log는 참조에러가 발생하게 되고 바로 아래 라인에서 '정'으로 초기화를 해줌과 동시에 호이스팅이 된 변수 a는 temporal deadzone에서 빠져나오게 되어 참조가 가능한 상태가 된다. 때문에 마지막 줄의 console.log는 정상적으로 값이 출력이 된다.
let과 const가 호이스팅이 정말로 되는지 어떻게 알까?
아래 코드를 보자
let a = 5;
let b = 10;
{
console.log(a); // 5
}
{
console.log(b); // Ref Error
let b = 20;
console.log(b); // 20
}
먼저 첫번째와 두번째 라인이 호이스팅 후 초기화까지 되어 참조를 할 수 있는 상태가 되었다고 가정을 하면 console.log(a)는 5가 나오는 것이 정상이다.
하지만 만약 호이스팅이 되지 않았다면 첫번째 console.log(b)는 10이 나와야 하는 것이 정상이지만 let은 블록 스코프에서 동작하기 때문에 해당 블록안에서의 호이스팅이 발생하게 되어 b는 초기화 전 참조가 불가능한 상태가 된다. 때문에 첫번째 console.log(b)는 참조에러가 발생하게 된다.
참고 포스팅
https://developer.mozilla.org/ko/docs/Glossary/Hoisting
https://gmlwjd9405.github.io/2019/04/22/javascript-hoisting.html