호이스팅에 대하여
이상한 avascript 문제
1sayHello(); // 정상 동작?
2
3function sayHello() {
4 console.log('hello');
5}
6위의 코드를 보면, 선언보다 먼저 sayHello()를 호출합니다.
미리 스포하자면, 위의 코드는 정상동작합니다.
1sayHello();
2
3var sayHello = function () {
4 console.log('hello');
5};
6마찬가지로 선언보다 먼저 호출한 경우인데, 이번에는 에러가 발생합니다.
둘 다 sayHello를 선언 전에 호출했는데, 왜 결과가 다를까요?
이 문제를 해결하려면, 호이스팅을 이해해야 합니다.
호이스팅(Hoisting)이란?
함수 및 변수 선언이 유효범위 최상단으로 끌어올려지는 현상을 호이스팅이라고 부릅니다.
실행 컨텍스트가 생성되면, JS는 코드를 실행하기 전에 먼저 변수 정보를 수집합니다.
이 때, var로 선언한 변수와 function으로 선언한 함수의 선언 및 정의 정보가 수집됩니다.
이러한 자바스크립트 엔진의 동작 때문에, sayHello 함수 정의가 호이스팅되어 정상적으로 호출할 수 있었던 것 입니다.
그렇다면, 왜 var sayHello = function() {...}은 에러가 발생한걸까요?
함수 선언식 vs 함수 표현식
그 이유는 함수 선언식과 표현식의 차이 때문입니다.
1sayWow(); // (3) → "wow" 출력
2sayYeah(); // (5) → TypeError 발생!
3
4var sayYeah = function () {
5 // (1) 선언, (6) 대입
6 console.log('yeah');
7};
8
9function sayWow() {
10 // (2) 선언과 동시에 정의(호이스팅)
11 console.log('wow'); // (4)
12}
13실행 순서를 따라가 보면 이렇습니다.
- 실행 컨텍스트가 생성될 때,
sayYeah변수는 선언만 수집됩니다. 값은undefined입니다. - 반면
sayWow함수는 선언과 정의가 동시에 수집됩니다. - 따라서
sayWow()는 선언 전에 호출해도 정상 동작하지만,sayYeah()는 대입 전에 호출하면 에러가 납니다.
| 구분 | 호이스팅 시 | 선언 전 호출 |
|---|---|---|
함수 선언식 function f() {} | 선언 + 정의 모두 수집 | ✅ 가능 |
함수 표현식 var f = function() {} | 변수 선언만 수집 (값은 undefined) | ❌ 에러 |
정리하면, 함수 선언식은 선언과 정의가 동시에 수집되어, 해당 코드를 만나기 전에도 문제 없이 호출이 가능하지만, 함수 표현식은 변수 선언만 수집되므로 호출할 수 없습니다.
let/const와 TDZ
그렇다면 var을 사용한 선언만 호이스팅되는걸까요?
var만 호이스팅된다고 오해하기 쉽지만, let과 const도 호이스팅이 일어납니다.
하지만 큰 차이가 있습니다.
1console.log(a); // undefined
2console.log(b); // ReferenceError: Cannot access 'b' before initialization
3
4var a = 1;
5let b = 2;
6var는 선언과 동시에undefined로 초기화됩니다.let/const는 선언은 수집되지만, 초기화는 코드상 선언부에 도달해야 이루어집니다.
선언부에 도달하기 전까지의 구간을 TDZ(Temporal Dead Zone, 일시적 사각지대) 라고 부릅니다.
이 구간에서 변수에 접근하면 ReferenceError가 발생합니다.
1// TDZ 시작
2console.log(x); // ReferenceError
3
4let x = 10; // 이 줄에서 TDZ 종료, 초기화 완료
5// TDZ 종료
6| 구분 | 호이스팅 | 초기화 시점 | 선언 전 접근 |
|---|---|---|---|
var | ✅ | 호이스팅 시 undefined | ⚠️ undefined 반환 |
let | ✅ | 선언부 도달 시 | ❌ ReferenceError |
const | ✅ | 선언부 도달 시 | ❌ ReferenceError |
정리
- 함수 선언식은 선언과 정의가 함께 호이스팅되어 선언 전 호출이 가능합니다.
- 함수 표현식과
var는 선언만 호이스팅되고, 값은undefined입니다. let/const는 호이스팅이 일어나지만 TDZ로 인해 선언 전 접근 시 에러가 발생합니다.
var보다 let/const를 권장하는 이유 중 하나가 바로 이 차이입니다.
var는 선언 전에 접근해도 undefined를 반환하기 때문에 개발자가 자신의 실수를 알아차리기 어려울 수 있습니다.
반면 let/const는 TDZ 덕분에 선언 전 접근 시 즉각 ReferenceError를 던집니다. 덕분에 바로 알아차릴 수 있습니다.
마치며
솔직히 요새는 var을 거의 사용하지 않기 때문에, 실무에 크게 도움이 되는 내용은 아닙니다. ☺️ 하지만 이 매커니즘을 알면 스코프 체인, this 바인딩 같은 심화 개념을 배울 때 수월하게 이해할 수 있어서 다루어보았습니다. 또 혹시 모릅니다. 레거시 코드를 뜯어봐야할 때 도움이 될지도 모릅니다. 😩 긴 글 읽어주셔서 감사합니다.