JavaScript에서 Hoisting이란 무엇인지 이해해보자.
Hoisting이란, 코드 실행되기 전에 variables, function, class와 같이, 선언된 모든 식별자들의 선언(declarations)을 유효범위내 스코프 최상단으로 옮기는 JavaScript의 작동 메커니즘을 의미한다.
무슨 말인지 와닿지 않는다. 하나씩 중요한 키워드들을 분석하면서 실제로 뭘 의미하는지 뜯어보자.
(이는 JS엔진의 동작방법에 대한 지식이 선행된다면 매우 이해하기 편해질 것이다. JS엔진에 대한 정보는 이전에 작성한 글을 참고하자.)
1. 코드가 실행되기 전
JS엔진은 JS 코드를 만나게 되면, Execution Context를 생성하게 되는데 이 Execution Context는 두 단계(Creation Phase, Execution Phase)를 지나게 된다.
위의 정의에서 코드가 실행되기 전이라는 것은, Creation Phase를 의미한다. (현재 포스트를 쭉 읽어도 이해 될 수 있지만, 궁금하다면 이전에 작성한 글을 참고하자.)
2. 선언된 모든 식별자들
Hoisting에 대해 잘 알지 못할 때에는 var와 함수선언만 호이스팅이 발생하는 줄 알았다. 하지만, 호이스팅은 var나, 함수선언 에서만 발생하는 현상이 아니라, class, function, let, const와 같은 JS의 모든 식별자들에게 발생하는 현상을 의미한다.
왜 이런 착각을 하게 됐을까?
아래 코드를 잠깐 살펴보자.
{
console.log(foo);
let foo = 'Error will occur';
console.log(boo);
var boo = 'hoisting'
}
let으로 선언한 변수를 선언 이전에 사용하려고 하면 Reference Error가 발생하지만,
var로 선언하거나, function의 경우에는 Reference Error가 발생하지 않고, undefined로 나오는 것을 볼 수 있다.
이전에 나는 이러한 현상을 보고, var와 function에만 호이스팅이 발생한다는 착각을 했었다.
하지만, 거듭 말했듯이, 호이스팅은 JS의 모든 식별자들을 대상으로 일어난다.
3. 선언(decleration)
호이스팅은 결국 식별자들의 선언을 끌어 올리는 것을 의미한다.
선언이 무엇인지 정확히 알기 위해서는 변수가 어떻게 생성되는지 간단하게라도 알 필요가 있다.
변수의 생성 단계
변수는 다음의 3가지 단계를 거치면서 생성된다.
변수 선언(decleration)
변수 선언 단계에서는 변수 객체를 실행 컨텍스트에 등록한다.
변수 초기화(initialization)
등록된 변수를 위한 메모리 공간을 확보한다. 변수에 undefined를 할당하여 해당 변수를 초기화 한다.
변수 할당(assignment)
초기화 단계를 마쳐 undefined가 할당된 변수에 원하는 값을 할당한다.
자, 변수는 이렇게 3가지의 단계를 거치면서 생성되는데, 이 중 딱 한가지 변수 선언단계, 즉 변수 객체들을 코드가 실행되기 전에 Execution Context에 등록을 해준다는 것이다.
초기화 단계를 거치지 않은 변수는 어떠한 값도 가지고 있지 않게 되고 이에 따라 Reference Error가 발생하게 되는 것이다.
하지만, var는 특이한 녀석이라 선언과 초기화가 동시에 발생하게 된다. 따라서, 아직 변수에 할당은 되지 않았지만, undefined라는 값이 존재하기 때문에(초기화) 에러가 발생하지 않는 것이다.
참고로, function의 선언의 경우, 선언과 동시에 그 함수의 전체 코드가 값으로 mapping되므로, 선언 이전에도 바로바로 사용할 수 있게 된다.
4. 유효범위내 스코프 최상단으로 옮기는
앞서 말했듯이, JS엔진은 가장 먼저 Creation Phase를 통해 Execution Context를 만들고, 선언된 식별자들을 스캔하여 해당 Execution Context에 저장한다. 그리고 실행코드를 Execution Stack에 차곡차곡 쌓고, 실행시키게 된다.
(Execution Context가 실제로 어떤 모습인지, Execution Stack이 실제로 어떻게 작동하는지에 대해서는 이전에 작성한 글을 참고하자.)
실제 코드로 이해도 체크하기
let foo = 1;
{
console.log(foo);
let foo = 2;
}
위 코드를 실행하면 어떤 결과가 발생하게 될까?
만약, 필자가 위에서 정리한 호이스팅에 대한 개념을 이해했다면, Reference Error가 발생할 것이라는 것을 예측 할 수 있었을 것이다.
혹, 이해되지 않는다면 에러의 발생 이유는 아래와 같다.
설명의 편의를 위해, 글로벌 변수 foo는 foo(g), 스코프 안에 있는 변수 foo는 foo(l)이라고 지칭하자.
console.log는 foo(l)이 속한 스코프에 있다. 따라서, 콘솔을 찍으려고 하게 되는 foo는 foo(g)가 아닌 foo(l)이 될 것이다.
만약 이부분이 이해가 안된다면 스코프의 개념에 대해 다시 공부하는 것이 필요할 것이다.
그렇기 때문에 foo(l)에 접근하게 되는데, 이때 foo(l)은 호이스팅되어 선언만 되어 있을 뿐, 초기화도 할당도 되어 있지 않다.
그래서 결론적으로 Reference Error가 발생하게 되는 것이다.
참조 링크
https://www.freecodecamp.org/news/execution-context-how-javascript-works-behind-the-scenes/
'JavaScript' 카테고리의 다른 글
마우스 휠 이벤트 등록(js) (0) | 2022.06.29 |
---|---|
Mixed Content 문제 해결하기(https에서 http 사이트로 요청) (1) | 2022.06.15 |
Execution Context(실행 컨텍스트, feat JS엔진 작동 원리) (0) | 2022.05.16 |
json [object Object] 출력 (0) | 2022.04.13 |
new Set으로 배열 중복값 제거(with 퍼포먼스 테스트) (0) | 2022.04.11 |