
호이스팅이란?
💡 호이스팅이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것
주로 변수 또는 함수의 선언을 해당 스코프의 최상단으로 끌어올리는 것이라고 설명하곤 한다.
var, let, const란
var
, let
, const
란 변수 선언 방식으로 다음과 같다.
var | let | const | |
---|---|---|---|
스코프 | 함수 | 블록 | 블록 |
재할당 | O | O | X |
재선언 | O | X | X |
const
의 경우 반드시 선언과 동시에 초기화를 해야한다.
스코프(Scope)
스코프란, 식별자 접근 규칙에 따른 유효 범위를 말한다.
스코프는 다음에 자세히 게시글로 작성하고, 여기서는 블록(중괄호) 레벨 스코프
, 함수 레벨 스코프
에 대해서만 다룬다.
-
블록 레벨 스코프
한 쌍의 중괄호로 이루어진 스코프(ex: if문, for문, while문 등)로 블록 내에서 생성된 변수는 해당 블록 내에서만 참조 가능
-
함수 레벨 스코프
말그대로 함수 내 생성된 지역 스코프로, 함수 내에서 생성된 변수는 함수 내에서만 참조 가능
🚨 화살표 함수는 함수 스코프가 아닌 블록 스코프
다음은 블록 레벨 스코프, 함수 레벨 스코프 코드 예제이다.
for (let index = 0; index < 10; index++) {
// 생략
}
console.log(index); // ReferenceError
// let의 경우 블록 스코프라 이 위치에서 참조 불가
// for 블록 내에서만 참조가 가능
for (var index = 0; index < 10; index++) {
// 생략
}
console.log(index); // 9
// var의 경우 함수 스코프만 따르므로 for 블록 바깥에서 참조 가능
예제
var
하단의 코드는 정상적으로 동작한다. 왜일까?
cat = "meow";
console.log(cat); // "meow"
var cat;
그 이유는, 호이스팅이 일어나 실제로는 이렇게 동작하고 있기 때문이다.
var cat;
cat = "meow";
console.log(cat); // "meow"
var의 스코프의 최상단으로 변수의 선언이 끌어올려진 모습
function
함수의 경우에도 호이스팅이 일어난다. 다만, 함수 선언식으로는 호이스팅 되지만 함수 표현식으로는 호이스팅이 되지 않는다.
/* 함수 선언 */
foo(); // "bar"
function foo() {
console.log("bar");
}
/* 함수 표현식 */
baz(); // TypeError: baz is not a function
var baz = function () {
console.log("bar2");
};
함수 표현식은 왜 호이스팅이 되지 않을까? 이유는 변수에 있다.
var baz;
baz(); // TypeError: baz is not a function
baz = function () {
console.log("bar2");
};
변수 선언이 맨 위로 끌어올려져 baz
실행 당시에는 baz
의 값은 undefined
이기 때문이다.
let, const
let
과 const
는 어떨까?
let
과 const
로 선언한 변수도 호이스팅 대상이지만, var
와 다르게 호이스팅 시 undefined
로 변수를 초기화하지는 않는다. 따라서 변수의 초기화를 수행하기 전에 읽는 코드가 먼저 나타나면 예외가 발생한다(TDZ).
let foo = 1; // 전역 변수
{
console.log(foo); // ReferenceError
let foo = 2;
}
let
으로 선언한 변수가 호이스팅되지 않는다면 전역 변수foo
가 정상적으로 출력되어야한다. 그러나 호이스팅이 발생하기 때문에 ReferenceError가 발생한다.
☠️ TDZ(Temporal Dead Zone, 시간상 사각 지대)
변수 스코프의 맨 위에서부터 변수의 초기화 완료시점까지를 TDZ라고 부른다. 시간상 사각지대인 이유는 코드의 위치가 아닌 코드의 실행 시간에 영향을 받기 때문이다.
{
// TDZ 시작
const func = () => console.log(letVar);
// 여기서 func 호출 또는 letVar에 접근 시, ReferenceError 발생
let letVar = 3; // TDZ 종료
func(); // TDZ 밖에서 호출하여 ReferenceError 발생하지 않음
}
- TDZ의 영향을 받는 구문(선언 이후에 사용해야 함)
const
, let
, class
, constructor 내부의 super()
, 기본 함수 매개변수
-
constructor 내부의 super()
부모 클래스를 상속받았다면 super() 호출 전까지 this 바인딩이 TDZ에 있음
-
기본 함수 매개변수
const a = 2; function square(a = a) { return a * a; } // Does not work! square(); // throws `ReferenceError`
기본 매개변수
a
가 선언되기 이전에a = a
우변에서 사용됨(기본 매개변수는 선언 및 초기화 다음에 사용되어야함)const init = 2; function square(a = init) { return a * a; } // Works! square(); // => 4
-
TDZ의 영향을 받지 않는 구문(현재 스코프 내에서 호이스팅 영향을 받기 때문)
var
,function
,import
// 이게되네 myFunction(); import { myFunction } from "./myModule";