[JS] Hoisting
호이스팅이란?
'끌어올리다'라는 뜻으로 인터프리터가 변수와 함수의 메모리 공간을 선언하기 전에 미리 할당하는 것이다.
* JS는 인터프리터 방식이며, 현대에는 JIT compiler를 사용한다 = 자바와 유사함
아래 예시 코드를 보면 코드를 위에서부터 순차적으로 실행하기 때문에 test()는 에러를 발생시킬 것 같지만, 실제로 실행해보면 문제없이 동작하는 것을 볼 수 있다.
호이스팅으로 인해 위에서 호출된 test()가 함수의 메모리 주소를 가지고 있기 때문에 가능한 일이다.
test();
function test() {
console.log("hoisting");
}
변수 호이스팅에서는 함수와 달리 메모리 공간만을 할당하고 값까지 할당해주지는 않는다.
그렇기 때문에 console.log를 출력했을 때 에러가 발생하지는 않지만, 값이 들어있지 않아 undefined가 발생하게 된다.
console.log(name); // undefined
var name = "hoisting";
console.log(name); // hoisting
호이스팅을 사용하는 이유
위에서 살펴본 내용으로는 호이스팅이 있으면 개발자가 잘못 선언을 해도 에러가 발생하지 않아 문제를 찾기 힘들고, 예상치 못한 문제가 발생할 확률이 올라갈 것 같은 문제점이 보인다.
그럼에도 호이스팅을 사용하는 이유가 무엇일까?
호이스팅은 선언과 할당을 분리하여 유연성을 제공한다.
JS 함수를 선언하다 보면 함수 내부에서 다른 함수를 호출하게 되는데 이때 순서를 꼭 지켜야한다면, 개발할 때 굉장히 불편할 것이다.
예를 들어 호이스팅이 없을 때 함수가 아래와 같이 선언되어 있다면 a()에서 b가 정의되지 않은 함수라는 에러가 발생할 것이다.
코드가 짧다면 문제가 되지않지만 함수가 100개가 있다면? 순서를 맞추는 것이 쉽지 않을 것이다.
심지어 호이스팅을 사용하면 자기 자신을 스스로 호출하는 재귀함수도 사용할 수 있다.
function a(){
b();
}
function b(){
console.log("hoisting");
}
선언과 할당을 분리할 수 있다.
JS의 스크립트를 선언할 때 HTML의 어느 위치에 선언을 해도 대부분의 경우 잘 동작하는 것을 볼 수 있다.
이를 통해 코드를 작성할 때 선언과 할당을 자유롭게 배치할 수 있다.
스코프와 클로저를 구현하는 것에 도움을 준다.
스코프와 클로저에 대한 내용은 다른 포스팅에서 다루도록 하겠다.
호이스팅 문제를 해결하기 위해
JS는 ES6부터 호이스팅 문제를 해결하기 위해서 var 변수를 let과 const로 대체하기 시작했다.
대체했다고 해서 let과 const가 호이스팅이 적용되지 않는 것은 아니다.
여기서 TDZ(Temporal Dead Zone)이라는 개념이 같이 나오게 되는데 말 그대로 일시적으로 죽은 영역이다.
let과 const는 TDZ로 호이스팅이 안되는 것이 아닌 접근을 하지 못하도록 막는 것이다.
따라서 아래 코드는 결과적으로 ReferenceError를 발생시켜 호이스팅 문제를 방지한다.
console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "hoisting";
console.log(name); // hoisting