호이스팅은 Hoist(끌어 올리다) + -ing의 의미를 가지며 var(변수) 선언문, 함수 선언문 등을 해당 스코프의 상단으로 옮긴 것 처럼 동작하는 특징을 말한다.
자바스크립트에서는 모든 선언(var, let(ES6), const(ES6), function, class)을 호이스팅하는데 변수 호이스팅, 함수 호이스팅 두가지로 나뉜다.
호이스팅 규칙
- 선언된 함수는 해당 스코프 상단에서 참조, 호출이 가능하다.
- var 선언 방식은 해당 스코프 상단에서 참조, 할당이 가능하다.
- let, const 선언 방식은 상단에서 참조, 할당이 불가능하다.
변수 호이스팅
변수 호이스팅을 살펴보기 전 아래 코드를 한번 보자면,
console.log(one); // 1. undefined
var one = 123;
console.log(one); // 2. 123
{
var one = 456;
}
console.log(one); // 3. 456
// var 선언 키워드는 중복 선언이 가능하다.
최상단의 console.log(one)에서 one은 선언되지 않았기 때문에 ReferenceError: one is not defined이 나와야하지만 undefined이 출력된다.
이는 자바스크립트와 다른 C언어와의 차이점으로 자바스크립트에선 모든 선언문이 호이스팅된다.
자바스크립트는 앞서 말한 모든 선언문이 선언되기 이전에 참조가 가능하다.
변수 호이스팅은 세 가지의 단계를 가진다.
1. 선언 단계(Declaration phase)
- 파싱하는 과정에서 변수 객체(Variable Object)가 변수에 대한 식별자들을 수집한다.
- 이 변수 객체는 스코프가 참조하는 대상이 된다.
2. 초기화 단계(Initialization phase)
- 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.
- 이 단계에서 변수는 undefined로 초기화 된다.
3. 할당 단계(Assignment phase)
- undefined로 초기화된 변수에 실제 값을 할당한다.
var
키워드는 선언과 초기화 단계가 동시에 이루어진다. (그렇기 때문에 위의 예제에서 1번이 undefined이 된 것.) 즉 해당된 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보하고 undefined으로 초기화 단계를 거친다.
즉, 변수가 선언되기 이전 console 등으로 접근했을 때 스코프 안에 변수가 존재하여 에러는 발생하지 않고, undefined을 반환한다. 이후에 변수가 선언된 곳에 도착해서야 값의 할당이 이루어 진다.
그에 반해 let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다.
// 스코프의 선두에서 선언 단계가 실행된다.
// 아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 없다.
console.log(one); // ReferenceError: one is not defined
let one; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(one); // undefined
one = 'Hi'; // 할당하는 부분에서 할당 단계가 실행된다.
console.log(one); // 'Hi'
위의 경우처럼 모두 분리되어 단계가 실행된다.
즉, 초기화 단계 이전에 변수에 접근하려할 때는 참조 에러(ReferenceError)가 발생하고,
선언된 위치에서 초기화가 일어난 뒤 할당하는 부분에서 할당 단계가 실행된다.
이때 선언단계와 초기화 단계 사이를 일시적 사각지대(:TDZ(Temporal Dead Zone))이라 한다.
const
도 let
과 마찬가지로 호이스팅이 발생할 시 선언만 이루어지고 실행 시점에서 선언을 만날 때까지 초기화는 이루어지지 않는다. 일시적 사각지대에선 변수에 대한 메모리가 존재하지 않기 때문에 스코프의 상단에서 참조, 할당이 불가능하다.
함수 호이스팅
함수 호이스팅을 알기 위해서는 함수 선언식과 함수 표현식을 알아야하는데, 간단히 말해 함수 선언식은 호이스팅에 영향을 받지만, 함수 표현식은 호이스팅에 영향을 받지 않는다.
함수 선언식
함수 선언식은 함수를 독립적으로 선언한다. 함수의 이름이 지정되어 있지 않으면 스코프에 상단에서 호출할 수 없다.
function add(a, b) {
return a + b
}
console.log(add(1, 2) // 3
함수 선언식의 경우엔 상단으로 호이스팅되어 함수의 유효범위가 스코프의 상단부터 시작한다.
console.log(add(1, 2)) // 3
// 함수가 아래에 있더라도 상단에서 사용가능하다.
function add(a, b) {
return a + b
}
함수 표현식
함수 표현식은 하나의 함수를 만들고 이를 변수에 할당하는 것이다.
const add = function (a, b){
return a + b;
}
console.log(add(1, 2)) // 3
하지만 아래의 경우처럼함수 표현식은 함수 선언식과는 다르게 호이스팅이 되지 않는다.
console.log(add(1, 2)) // ReferenceError: add is not defined
const add = function (a, b){
return a + b;
}
그렇다면 함수 선언식과 표현식 중 무엇이 더 옳은 방법이라고 볼 수는 없지만 함수 선언식처럼 함수가 선언되기 전에 사용된다면 규모가 있는 프로젝트에선 코드의 구조가 복잡해질 것이다.
그렇기 때문에 보통의 경우 함수 표현식의 사용이 권장된다.
https://tecoble.techcourse.co.kr/post/2021-04-25-hoisting/
https://poiemaweb.com/js-data-type-variable#24-변수-호이스팅variable-hoisting