JavaScript로 개발을 하거나 JavaScript로 짜여진 코드를 많이 본 사람들은 한번쯤 궁금했을 개념 hoising에 대해 알아보자.
Hoising이란 변수나 함수가 선언되기 전에 이를 사용할 수 있는 현상을 말한다.
프로그램이 실행되기 전 JavaScript는 코드를 스캔하여 어떤 변수가 선언되어 있는지 확인하고 각 변수가 variable environment에 미리 생성된다. 이 작업은 각 함수에 대한 실행 컨택스트가 생성되는 단계에서 일어난다.
다음은 변수 및 함수에 따라 hoisting이 어떻게 적용되는지 표를 통해 살펴보자. Hoising과 더불어 초기값과 스코프에 대한 내용도 함께 정리했다.
위 표를 참고하여 다음 코드를 살펴보자.
// undefined
console.log(language);
// ReferenceError: Cannot access 'operatingSystem' before initialization
console.log(operatingSystem);
// ReferenceError: Cannot access 'socialMedia' before initialization
console.log(socialMedia);
// 5
console.log(addNum(3, 5));
var language = 'JavaScript';
let operatingSystem = 'Windows';
const socialMedia = 'Instagram';
// function declaration
function addNum(a, b) {
return a + b;
}
1. var 변수: 역시 hoisting이 적용되어 변수에 접근 가능하지만 초기값은 변수에 대입된 값이 아닌 undefined로 나타난다.
2. let / const 변수: hoisting이 적용되지 않으며 선언되기 전에 let이나 const로 선언된 변수에 접근하려는 경우 ReferenceError가 발생한다.
- 이때, 이 변수들은 TDZ(Temporal Dead Zone)에 들어가 실제로 선언될 때까지 머무른다.
3. 함수 선언식: Hoisting이 적용된다. 즉, 실제로 함수가 선언되기 전 함수 선언식을 사용할 수 있다는 의미이다. 이때 초기값은 실제 함수이며 스코프는 블록 (block scope)를 따른다.
- 함수 선언식으로 선언된 addNum가 선언되기 전 8번 줄에서 addNum 함수를 불러도 문제없이 함수가 실행되는 것을 볼 수 있다.
4. 마지막으로 표와 코드 예시는 없지만 함수 표현식이나 ES6에 소개된 화살표 함수는 어떨까? 이 경우는 함수가 어떤 키워드로 선언되었는지에 따라 달라진다. var 키워드의 경우 var 변수과 같이 hoisting이 적용되고 let이나 const로 선언된 함수의 경우는 hoisting이 적용되지 않고 역시 함수 선언이 완료될 때까지 함수는 TDZ에 머무른다.
앞서 간단히 소개한 TDZ를 깊게 이해하기 위해 아래의 간단한 코드를 살펴보자.
const country = 'South Korea';
if (country === 'South Korea') {
// ReferenceError: cannot access 'nationality' before initialization
console.log(`You are a ${nationality}`);
const age = 10;
console.log(age);
const nationality = 'Korean';
// ReferenceError: emailAddress is not defined
console.log(emailAddress)
}
* 위 코드에서 7번 줄에 선언한 nationality 변수에 4번 줄에서 접근하려고 할 때 초기화되지 않았다는 ReferenceError가 발생한다. const 키워드로 선언한 변수에 hoisting이 적용되지 않은 것을 알 수 있다. 이때, 4번줄과 7번줄 사이는 nationaliry 변수의 Temporal Dead Zone으로써 이 구역에서는 nationality 변수가 없다고 간주한다. 따라서 ReferenceError가 발생하는 것이다.
* 그렇다면 TDZ에 있는 변수는 아예 선언되지 않은 변수와 같을까? 그렇지 않다. 다시 위 코드로 돌아가보면 emailAddress 변수는 선언되지 않았으나 10번 줄에서 사용하려 하고 있다. 역시 ReferenceError가 발생하지만 오류 메세지가 그 전 nationality 변수와 다르다. emailAddress는 아예 정의되지 않았다는 내용의 메세지를 보인다.
* hoisting이 적용되지 않는 const 키워드로 선언한 nationality 변수와 emailAddress 변수에 접근하고자 할 때 동일하기 오류가 발생하지만 JavaScript 엔진은 nationality 변수가 7번 줄에서 선언될 것임을 알고 있다. 그리고 마침내 7번 줄에 다다르면 nationality 변수는 TDZ에서 벗어나 사용 가능 변수가 된다.
각 let, const 변수는 별개의 TDZ를 가지며 이 구역은 해당 변수의 스코프 시작점에서 부터 변수가 선언되는 지점까지 존재하고 사라진다. 변수가 TDZ에 존재할 때 해당 변수에 접근할 수 없으며 접근을 시도하면 에러가 발생한다. 하지만 이때에도 JavaScript 엔진은 해당 변수가 TDZ를 벗어나 선언되는 것을 알고 있기 때문에 아예 선언되지 않은 경우와 명확히 다르다.
그렇다면 TDZ가 왜 ES6에 추가되었을까? 선언되지 않은 변수에 접근하려 하는 할 때 발생할 수 있는 버그를 최소화하기 위함이 가장 큰 이유이다. 이는 변수나 함수가 선언되기 전 이에 접근해야 하는 희귀한 경우를 제외하고 코드의 가독성이나 오류 최소화를 위해 지양해야하는 practice이다.
'JAVASCRIPT' 카테고리의 다른 글
생성자 함수와 Prototype (0) | 2020.11.17 |
---|---|
Closure의 이해 (0) | 2020.11.17 |
this 키워드 (0) | 2020.11.16 |
Scope와 Call Stack (0) | 2020.11.14 |
SCOPE의 이해 (0) | 2020.11.14 |