☝ 호이스팅
자바스크립트 엔진은 변수와 함수의 선언을 선언단계와 초기화 단계, 두 단계로 나누어 처리한다. 이렇게 두 가지 단계로 나누어 처리하다 보니, 마치 선언을 스코프 최상단으로 올려서 하는 것처럼 보이게 되는데, 이것을 호이스팅이라고 한다.
변수와 함수 두가지를 나눠서 앞서 정리했던 실행 컨텍스트와 함께 호이스팅을 정리해보려 한다.
🙋♂️선언이란
먼저 선언에 대해 정리해보면, 변수와 함수를 선언한다는 것은 해당 변수와 함수에 대한 정보를 저장할 수 있는 메모리를 할당하고 할당한 메모리와 변수를 연결하는 과정이다. 이러한 선언은 다음과 같이 두 단계로 나눌 수 있다.
- 선언 단계: 해당 변수와 함수를 등록하는 것
- 초기화 단계: 해당 변수와 함수에 메모리를 할당하고 값을 저장하는 과정
이러한 두 단계가 변수와 함수에서 어떻게 다르게 작동하는지에 대해 알아보자.
변수 선언의 호이스팅
변수 선언은 "var"와 "const와 let"으로 구분할 수 있다. 모두 동일하게 선언 단계에서 변수 이름을 등록하지만, var의 경우 선언 단계와 초기화 단계가 동시에 진행되는 특징을 가진다. 이때 var는 암묵적으로 undefined으로 할당된다. const와 let은 다르게 선언 단계만 진행되기 때문에 값을 가져올 수 없어 referenceError가 발생한다. 이렇게 const와 let의 경우, 변수 선언문을 자바스크립트 엔진이 읽을 때까지 값을 불러올 수 없는 구간이 생기는데 이것을 "TDZ(Temporal Dead Zone)"라 부른다.
정리하면 둘 다 선언 단계에서 변수를 등록하기 때문에 호이스팅이 일어나지만, var는 const/let과 다르게 초기화단계도 함께 진행하기 때문에 변수 선언문 전에 참조가 가능하다는 특징을 가진다.
console.log(first) //undefined
console.log(second) //ReferenceError: Cannot access 'second' before initialization
var first = 80
const second = 90함수 선언의 호이스팅
함수 선언은 "함수 선언문"과 "함수 표현식"으로 구분할 수 있다. 함수 선언도 동일하게 선언 단계와 초기화 단계로 나뉘는데, 함수 선언문은 변수 선언의 var와 같이 선언 단계와 초기화 단계가 동시에 진행된다. 이때 함수 선언문은 var와는 다르게 undefined가 아니라 함수 객체로 초기화되어 함수를 바로 사용할 수 있다.
함수 표현식은 변수에 함수를 할당하는 것이므로 "변수 선언과 동일하게" 진행된다. var 함수 표현식은 var 변수 선언과 동일하게 undefined로 초기화 단계가 선언단계와 함께 진행되고, const와 let 함수 표현식은 함수 선언문을 자바스크립트 엔진이 읽을 때까지 값을 불러올 수 없다.
console.log(multiply(1, 2)) // 2
console.log(sum) // undefined
console.log(sub) // ReferenceError: Cannot access 'sub' before initialization
var sum = (a, b) => a + b
const sub = (a, b) => a - b
function multiply(a, b) {
return a * b
}이때 var 함수 표현식은 var 변수 선언과 동일해, 함수 객체가 할당되긴 전이기 때문에, 함수로 실행하면 typeError가 발생한다.
console.log(sum(1, 2)) // TypeError: sum is not a function
var sum = (a, b) => a + b이제 실행 컨텍스트를 이용해 자세한 내부 동작을 정리해보자
📰 실행 컨텍스트
실행 컨텍스트는 "코드가 실행되기 위한 환경"을 의미한다. 자바스크립트 코드를 자바스크립트 엔진이 읽을 때 항상 먼저 해당 스코프에 해당하는 실행 컨텍스트를 만든 후에, 평가 과정과 실행 과정을 진행한다. 이 두 가지 과정 중 "평가 과정이 변수와 함수 선언의 선언 단계"를 포함하고, "실행 과정이 변수와 함수 선언의 초기화 단계"를 포함한다.
평가 과정을 다시 정리해보면, 실행 컨텍스트가 생성되고 lexical 환경을 만들어 실행 컨텍스트와 바인딩을 한다. 이때 lexical 환경의 구성 요소로 환경 레코드와 외부 렉시컬 환경에 대한 참조 두 가지가 있는데, 여기서 환경 레코드가 우리가 선언한 "변수와 함수가 저장되는 곳"이다.
환경레코드는 다시 "객체 환경 레코드"와 "선언적 환경 레코드"로 나뉘어, var로 된 변수와 표현식, 함수 선언문은 "객체 환경 레코드"에 저장되고 const와 let으로 선언된 변수와 표현식은 "선언적 환경 레코드"로 구분되어 등록된다. 위에서 정리한 것처럼 평가 과정에서 var로 된 변수와 표현식은 초기화 단계를 함께 진행해 undefined으로 초기화가 되고, 함수 선언문은 함수 객체로 초기화가 된다.
이후 실행 과정에서 각각 할당할 값이 있을 경우에 새로운 값으로 재 할당이 되고, 이때 const와 let의 초기화 단계가 진행된다. 이렇게 선언 과정이 실행 컨텍스트의 진행 과정에 어떻게 정리되어있는지 알아보았다.
😁 한마디
정리하면 모든 변수와 함수선언에 호이스팅이 일어난다. 하지만 선언 단계 이후에 초기화 단계가 바로 일어나냐에 따라서 참조를 할 수 있냐 없냐로 다른 결과를 가지는 것을 알 수 있다.