코딩/Javascript

자바스크립트 호이스팅(Hoisting) 쉽게 설명

드리프트 2022. 4. 12. 17:38
728x170
Javascript Hoisting, 자바스크립트 호이스팅

 

 

안녕하세요?

 

지난 시간에 배운 Execution Context에 이어 이번에는 자바스크립트의 호이스팅(Hoisting)에 대해 알아보겠습니다.

 

먼저, 지난 시간의 핵심 부분만 복습하고 다음으로 진행하겠습니다.

 

자바스크립트의 모든 것은 바로 Execution Context안에 싸여 있다고 말할 수 있으며, 약간은 추상적인 콘셉트로 들릴지 모르겠지만 자바스크립트 모든 코드가 실행되는 주위 모든 환경의 정보를 전부 가지고 있는 커다란 컨테이너? 또는 박스?라고 볼 수 있습니다.

Execution Context는 두 가지 단계(Phase)를 가지는데요.

하나는 Memory Allocation Phase, 다른 하나는 Code Execution Phase입니다.

즉, 메모리 할당 단계와 코드 실행 단계를 가진다고 보시면 됩니다.

1. 메모리 할당 단계에서는 자바스크립트의 모든 함수와 변수가 Execution Context의 메모리 컴포넌트 안에 키 밸류(Key-Value) 타입으로 저장되는 단계입니다.

이 단계에서는 자바스크립트는 함수 블록 전체를 메모리 블록에 카피하고, 변수일 경우는 placeholder 즉, 변수의 자리를 undefined 타입으로 할당하게 됩니다.

2. 코드 실행 단계에서는 그냥 코드 컴포넌트 안에서 한 줄 한 줄 순차적으로 코드가 실행될 뿐입니다.

우리가 호이스팅과 관련하여 눈여겨볼 단계는 바로 메모리 할당 단계입니다.

 

그럼, 테스트를 위한 세팅을 해볼까요?

 

오늘은 디버거를 이용할 예정이라 크롬을 이용하겠습니다.

 

먼저, 빈 폴더에 index.html 파일과 index.js파일을 만들고 index.html 파일에서 index.js파일을 읽어오도록 하면 됩니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Javascript Tutorial</title>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>

그리고 index.js파일은 우리가 테스트할 코드를 작성할 예정인데요.

 

크롬에서 index.html 파일을 읽으려면 다음과 같이 하면 됩니다.

 

npm install serve -g

serve .

npm 라이브러리 serve를 사용하는 방법이 있고요.

 

두 번째는 python을 이용하는 방법입니다.

 

python3 -m http.server 3000

꼭 파이썬 버전 3을 이용해야 합니다.

 

이제 크롬에서 localhost:3000을 열어 봅시다.

 

크롬 devtools인데요.

 

오른쪽 버튼 누르고 "검사"를 고르면 나옵니다.

 

위 화면에서 우리가 볼 곳은 바로 "소스"입니다.

 

일단 index.js 파일에 아래 코드를 넣어 줍시다.

 

console.log("Javascript Hoisting!");


var a = 5;
function AAA() {
  console.log("Hello Javascript!");
}

console.log(a);
AAA();

 

우리의 코드와 함께 콘솔 창에 실행결과 까지 나오고 있습니다.

 

맨 밑에 콘솔창 배열은 창 추가하시면 됩니다.

 

일단은 우리가 필요로 하는 거는 크롬의 디버거인데요.

 

위의 화면에서 소스코드 1행의 숫자 1에 마우스를 갖다 대고 오른쪽 버튼을 누르면 메뉴가 나오는데 중단점(breakpoint)을 추가하거나 아니면 그냥 왼쪽 버튼을 눌러 중단점을 추가할 수 있습니다.

 

위 그림과 같이 중단점이 잘 설정되어 있네요.

 

이제 화면을 리프레쉬합시다.

 

Ctrl + R이거나 맥에서는 Cmd + R 일 겁니다.

우리의 자바스크립트 코드가 중당점에서 일시중지되었다고 알려주네요

 

그리고 밑의 콘솔 창에도 아무것도 출력되지 않은 상태입니다.

 

변수자! 여기서 우리가 지난 시간에 배운 Execution Context의 첫 번째 단계인 메모리 할당 단계에 대해 그 말이 맞는지 찾아볼까요?

 

자바스크립트 코드가 처음 시작되면 그 전역(global) 스코프의 모든 변수와 함수가 메모리 컴포넌트에 저장된다고 했었는데요.

 

어떻게 확인할 수 있을 까요?

 

바로 위와 같이 파란색 표시된 "전역"을 누르면 됩니다.

 

이게 바로 Global Scope인데요.

 

한번 눌러볼까요?

 

위 그림에서 첫 번째에 AAA: f AAA()라고 있고 두 번째에 a: undefined이라고 있습니다.

 

Execution Context에 의해 함수는 위와 같이 고스란히 저장하고 변수는 undefined이라고 설정하고 있습니다.

 

이게 바로 자바스크립트의 호이스팅(Hoisting)입니다.

 

그러면 a라는 변수는 왜 undefined일까요?

 

바로 코드의 실행이 var a = 5;라는 행까지 못 갔기 때문입니다.

 

위 그림에서 맨 위에 디버거에서 일시 중지됨이란 글자와 그 옆에 아이콘 두 개가 있는데요.

 

마지막 화살표 같은 아이콘을 눌러봅시다.

 

 

그러면 위와 같이 코드의 실행 위치가 다음 위치로 이동하는데요.

 

4행이 음영 표시가 된 걸로 보아 코드의 실행 위치가 4행으로 이동했다고 알 수 있습니다.

 

그리고 콘솔 창에는 1행이 실행된 실행 결과가 나타났죠.

 

그리고 옆의 전역이란 화면에는 아직도 a: undefined이라고 나와 있습니다.

 

왜냐하면 4행이 실행 전이기 때문이죠.

 

자, 그럼 다시 맨 위의 화살표를 눌러 4행을 실행하고 5행으로 이동해 볼까요?

 

5행으로 이동하지 않고 9행으로 이동했습니다.

 

왜냐하면 5행부터 7행은 함수의 내용이기 때문에 함수가 실행되지 않는 이상 5행에서 7행은 코드 실행이 되지 않는 거죠.

 

그래서 바로 9행으로 코드의 실행 위치가 이동했습니다.

 

그리고 드디어 오른쪽의 전역 스코프에 a: 5가 저장되었네요.

 

여기까지가 바로 Execution Context의 메모리 할당 단계의 내용입니다.

 

메모리 할당 단계로 보면 호이 스팅의 원리가 쉽게 이해되시죠?

 

그럼 좀 더 어려운 내용으로 들어가 볼까요?

 

index.js 코드를 아래와 같이 바꿔 볼까요?

 

console.log("Javascript Hoisting!");

console.log(a);
AAA();

var a = 5;
function AAA() {
  console.log("Hello Javascript!");
}

 

a라는 변수의 값 저장이 일어나기 전에 console.log(a)로 인해 a라는 변수에 접근하는데요.

 

이 단계에서는 메모리 할당 단계에 의해 a는 undefined가 됩니다.

 

그리고 AAA() 함수의 실행은 정상 실행될 건데요.

 

왜냐하면 Execution Context의 메모리 할당 단계에서 함수는 어느 위치에 있든 그 내용이 메모리 할당 단계에서 메모리에 저장된다고 했기 때문입니다.

 

실행 결과를 볼까요?

 

 

Execution Context의 메모리 할당 단계를 이해했고 자바스크립트 호이스팅이 뭔지 이해하셨다면 위와 같은 결과는 쉽게 예측할 수 있을 겁니다.

 

그럼 다음으로 함수  AAA의 선언을 애로우 함수로 바꿔 볼까요?

 

console.log("Javascript Hoisting!");

console.log(a);
AAA();

var a = 5;
var AAA = () => {
  console.log("Hello Javascript!");
};

실행 결과는 심각한데요. 바로 에러가 났습니다.

 

왜 그런 걸까요? 디버거를 이용해서 살펴봅시다.

 

전역 스코프에 AAA: undefined와 a: undefined라고 나오네요.

 

왜 그런 걸까요?

 

바로 애로우 함수의 선언 방식이 함수의 이름을 먼저 변수명으로 저장하기 때문입니다.

 

코드의 실행이 애로우 함수의 실행 부분으로 넘어가기 전까지는 AAA는 그냥 var AAA처럼 변수인 것으로 인식되기 때문입니다.

 

그러면 AAA()라는 AAA 함수 실행 코드를 맨 밑으로 옮겨 놓고 디버거를 진행해 보겠습니다.

 

console.log("Javascript Hoisting!");

console.log(a);

var a = 5;
var AAA = () => {
  console.log("Hello Javascript!");
};
AAA();

일단 디버거를 이용해서 6행까지 왔습니다.

 

5행을 지나왔기 때문에 전역 스코프에 a: 5라고 표시되었네요.

 

이제 다시 디버거를 이용해서 다음 단계로 넘어가 볼까요?

 

이제 자바스크립트 엔진이 6행이 애로우 함수란 거 알았습니다.

 

그래서 전역 스코프에 애로우 함수 형식으로 함수를 저장했고요.

 

애로우 함수도 함수이기 때문에 아직은 실행이 되지 않았고 마지막으로 9행이 실행되면 AAA 함수가 실행되게 됩니다.

 

어떤가요?

 

이제 Execution Context의 메모리 할당 단계를 이용해서 자바스크립트의 호이스팅(Hoisting)을 이해할 수 있겠죠?

 

사실 예전에 쓴 자바스크립트 호이스팅 관련 블로깅 보다 Execution Context의 메모리 할당 단계를 이해해서 호이스팅을 이해하는 게 더 쉬운 거 같습니다.

 

마지막으로 이와 자바스크립트 디버깅할 때 Call Stack (호출 스택)에 대해 실제 어떻게 이뤄지는지 보고 갑시다.

 

위의 예에서 코드 실행을 9행까지 실행시키고 그다음은 아래와 같은 버튼을 누릅시다.

 

위 그림처럼 오른쪽 파란색 원으로 표시된 아래점 화살표를 누르면 디버거의 다음 실행이 함수 안으로 이동하게 됩니다.

 

그리고 오른쪽 파란색 네모 박스의 호출 스택은 (익명)이라고 나오는데요.

 

호출 스택이 현재 상태로는 Global 이기 때문입니다.

 

코드의 처음 실행이 바로 Global이기 때문에 아직 아무 곳에도 들어가지 않았다는 뜻입니다.

 

이제 파란색 아래점 화살표를 눌러볼까요?

 

어떻게 됐나요?

 

위 그림을 보시면 코드의 실행이 AAA 함수 안으로 이동했고 오른쪽 호출 스택은 바로 AAA라고 명시되고 있습니다.

 

우리가 함수를 실행하면 Execution Context가 또 생긴다고 했었죠?

 

바로 로컬이라는 범위(scope)가 생겨난 게 그 행동입니다.

 

이제 디버거의 아래점 버튼을 눌러 코드의 실행을 AAA 함수 안을 벗어나 볼까요?

 

범위(Scope)에 전역만 있고 로컬이 사라졌습니다.

 

그리고 호출 스택에도 AAA라는 게 사라졌습니다.

 

바로 함수 AAA 실행을 끝냈으니까 호출 스택에서 POP OFF 한 거죠.

 

이제 뭔가 자바스크립트가 작동하는 원리가 머릿속에 그려지시는가요?

 

오늘은 여기까지 알아보겠습니다.

 

그럼.

 

그리드형