호이스팅(Hoisting)이란 간단하게 말해서 자바스크립트에서 변수(var)와 함수(function) 선언을 컴파일 단계에서 메모리 공간에 끌어올리는 것을 말합니다.
자바스크립트는 코드를 실행하기 전에 먼저 전체 코드를 컴파일합니다. 이때, 변수와 함수 선언을 메모리에 먼저 올려놓고, 실행 시점에서 해당 변수와 함수를 참조하게 됩니다.
변수에서 호이스팅은 변수의 선언부만 끌어올리고, 초기화 부분은 원래 위치에 그대로 남게 됩니다. 이로 인해 변수를 선언하기 이전에 변수를 사용할 수 있게 되며, 값은 undefined로 초기화됩니다.
함수에서 호이스팅은 함수 전체를 끌어올리며, 선언부와 함수 내용 모두를 포함합니다. 따라서 함수를 선언 이전에 호출할 수 있습니다.
하지만, 호이스팅은 코드의 가독성을 떨어뜨리고, 예기치 못한 버그를 유발할 수 있습니다. 왜냐하면 변수나 함수의 선언이 아래쪽에 되어있어도 정상적으로 동작하기 때문입니다. 다른 언어들은 순서대로 코드를 작성해야 정상적으로 실행이 되는데, 자바스크립트는 호이스팅이라는 현상 때문에 선언보다 먼저 변수나 함수가 사용되어도 정상 동작합니다. 따라서 변수와 함수의 선언은 코드의 최상단에서 이루어지는 것이 좋습니다.
또한, ES6에서는 let, const 등의 변수 선언 방식과 arrow function을 포함한 새로운 함수 선언 방식을 도입하여 호이스팅으로 인한 문제를 최소화하고 있습니다.
하지만 let과 const가 호이스팅이 되지 않는 것처럼 보여지는 것이지 변수가 초기화 되기전에 RefenceError
를 발생시키는 것일 뿐 호이스팅은 됩니다.
이는 일시적 사각지대(TDZ)와 관련이 있습니다.
TDZ란 일시적인 사각지대란 뜻으로, 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말합니다.
자바스크립트에서 변수는 위의 사진처럼 선언, 초기화, 할당이라는 3가지 단계에 걸쳐 생성됩니다.
선언 단계 (Declaration phase) : 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계, 이 변수 객체는 스코프가 참조하는 대상이 된다.
초기화 단계 (Initialization phase) : 실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계
할당 단계(Assignment phase) : 사용자가 undefined로 초기화된 메모리의 다른 값을 할당하는 단계
var, let/const는 위의 과정을 거치며 생성이 되는 것이고 var와 let/const는 이 3단계에서 각각의 차이가 있습니다.
var 변수는 변수 선언전에 선언 단계와 초기화 단계를 동시에 진행합니다.
그래서 javascript는 실행 컨텍스트 변수 객체의 변수를 등록하고 메모리를 undefined로 만들어 버립니다.
그렇기 때문에 변수를 선언하기 전에 호출을 해도 undefined로 호출이 되는 호이스팅이 발생하는 것
입니다.
let 변수는 var 변수와는 다르게 선언 단계와 초기화 단계가 분리되어서 진행됩니다.
여기서 TDZ는 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말합니다.
즉 let 또한 선언전, 실행 컨텍스트 변수 객체에 등록이 되어 호이스팅이 되긴 되지만, 이 TDZ 구간에 의해서 메모리가 할당이 되지 않아서 참조 에러가 발생하는 것입니다.
참고로 함수는 3단계를 동시에 진행합니다.