호이스팅이 뭐야? 👀
- 호이스팅(Hoisting) 은 실제로 선언되기 전에 몇몇 타입의 변수들에 접근 가능하고 사용할 수 있는 것을 말한다.
- 마치 변수들이 마법처럼 스코프의 최상위로 이동하는 것처럼 보인다.
마법처럼 보이기도 하는, 이 호이스팅의 내부 동작 방식을 알기위해 먼저 변수에 대해서 알아보자.
변수 (Variable)
변수의 종류
ES6 이전 단계에서 사용하던 var
와 ES6부터 생긴 let
& const
가 있다.
사실 var
와 let
은 크게 다르지 않다.
대부분의 경우에는 둘을 바꾸어서 사용해도 문제가 되지 않는다.
그렇지만, 분명 다른 점이 존재한다!
var
는 한 번 선언된 변수를 다시 선언할 수 있다.
var name = 'Mike'
console.log(name) // Mike
var name = 'Jane'
console.log(name) // Jane
- 지금
name
이 두 번 선언되었는데, 전혀 문제가 되지 않는다. 같은 상황에서let
은? 🚨 문제가 된다!
var
는 선언하기 전에 사용할 수 있다.
console.log(name) // undefined
var name = 'Mike'
var
는 이 상황에서 에러를 일으키지 않는다.
var name // 선언
console.log(name) // undefined
name = 'Mike' // 할당
var
로 선언한 모든 변수는 코드가 실제로 이동하지는 않지만, 최상위로 끌어올려진 것처럼 동작한다. (호이스팅)console.log()
에서 undefined 값을 찍는 이유 : 선언은 호이스팅 되지만, 할당은 호이스팅 되지 않기 때문이다!name
이라는 변수만 호이스팅으로 인해 올려진 것이고, ‘Mike’라는 할당 값은 그 자리에 있는 것이다.
할당은 3번째 줄에서 처리가 된다.
- 그렇다면
let
의 경우는?
console.log(name) // ReferenceError
let name = 'Mike'
- 같은 상황에서
let
은 에러가 발생한다. 그렇다고let
이 호이스팅 되지 않는 것은 아니다. - 사실
let
과const
도 호이스팅 된다. - 다시 말하자면, 호이스팅은 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것처럼 행동하는 것을 말한다.
그런데 왜var
처럼 동작하지 않고, 에러를 반환하는 것일까?
💡 이유는 TDZ(Temporal Dead Zone) 에 있다!
결론부터 말하자면, TDZ 영역에 있는 것은 사용할 수 없다.
JS는 Top-down 방식으로 코드를 동작시키는데,
먼저 선언되지 않은 변수가 호출될 경우 해당 변수가 포함된 코드를 TDZ 영역으로 인식하게 된다.
let
과 const
는 TDZ의 영향을 받는 변수라서, 할당하기 전에는 사용할 수 없다.
console.log(name) // Temporal Dead Zone
const name = 'Mike' // 함수 선언 및 할당
console.log(name) // 사용 가능
TDZ가 있어서 코드를 예측 가능하게 하고, 잠재적인 버그를 줄일 수 있다.
실제 선언 전에 변수에 접근하는 방식은 매우 좋지 않은 습관이며 피해야 한다.
let age = 30
function showAge() {
console.log(age)
}
showAge()
- 현재 이 코드는 문제가 없다.
let age = 30
function showAge() {
console.log(age) // ReferenceError
let age = 20 // ✘
}
showAge()
- 여기서
let
은 호이스팅 되지 않는구나, 생각할 수도 있다. - 호이스팅은 스코프 단위로 일어난다.
여기서 스코프는 함수 내부인데let
으로 선언한 두 번째 age 변수가 호이스팅을 일으킨다.
만약 호이스팅이 되지 않았다면, 함수 바깥의 age = 30 값이 정상적으로 찍혔어야 한다.
변수의 생성 과정
1️⃣ 선언 단계
2️⃣ 초기화 단계
3️⃣ 할당 단계
var
는 선언 및 초기화 단계가 동시에 된다.
- 초기화 단계 : undefined를 할당해주는 단계
- 할당 전에 호출하면 에러를 내지 않고 undefined를 찍는다.
let
은 선언 단계와 초기화 단계가 분리되어 진행된다.
- 실행이 선언보다 먼저일 때, 호이스팅되면서 선언 단계가 이루어지지만 초기화 단계는 실제 코드에 도달했을 때
실행되기 때문에 Referrence Error가 발생하게 된다.
const
는 선언+초기화+할당 동시에 된다.
let name;
name = "Mike";
var age;
age = 30;
const gender;
gender = "male";
-
var
와let
은 선언만 해두고 나중에 할당하는 것을 허용한다. (나중에 값을 변경할 수 있기 때문에 당연하다) -
const
에서 SyntaxError 발생하는 이유는 선언하면서 바로 할당을 안했기 때문이다.
여기서 다시 생각해보는 스코프?!
-
var
: 함수 스코프 (function-scoped)- 함수 내에서 선언된 변수만 그 지역 변수가 되는 것이다!
-
let
&const
: 블록 스코프 (block-scoped)- 모든 코드 블록에서 선언된 변수는 코드 블록 내에서만 유효하며, 외부에서는 접근할 수 없다.
즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.
여기서 코드 블록은 함수, if문, for문, while문, try/catch문 등을 말한다.
- 모든 코드 블록에서 선언된 변수는 코드 블록 내에서만 유효하며, 외부에서는 접근할 수 없다.
function add() {
// Block-level Scope
}
if() {
// Block-level Scope
}
for(let i = 0; i < 10; i++;) {
// Block-level Scope
}
🏢 함수 스코프 vs 🧱 블록 스코프
const age = 30
if (age > 19) {
var txt = '성인'
}
console.log(txt) // '성인'
- if문 안에서
var
로 선언한 변수는 if문 바깥에서도 사용이 가능하다. : 함수 스코프 let
과const
는 중괄호 내부(블록 단위)에서만 사용 가능하다. : 블록 스코프
function add(num1, num2) {
var result = num1 + num2
}
add(2, 3)
console.log(result)
var
도 함수 내에서 선언되면 함수 바깥에서는 사용할 수 없다!
유일하게 벗어날 수 없는 스코프가 함수라고 생각하면 된다.
예측 가능한 결과를 내고, 버그를 줄이기 위해서
var
는 사용하지 않고let
과const
를 사용한다.
✨ 정리
호이스팅을 공부하면서, 변수 var
와 let
의 차이점에 대해서 확실히 알 수 있어서 좋았다.
자바스크립트 ES6 이후로만 계속 사용해왔어서 var
를 사용해본 적이 없어서 let
과의 차이에 대해서
뚜렷하게 알지 못했는데 이번 기회에 확실히 잡을 수 있었다! 🤓