본문 바로가기
개발(Development)/JS(자바스크립트)

[자바스크립트] 코드 실행 2단계와 변수/함수 생성 과정

by 카레유 2021. 4. 8.

자바스크립트는 소스 코드를 2단계로 실행합니다.

1 단계: 실행 컨텍스트 생성하고, 변수 등을 등록하는 단계

2 단계: 소스 코드를 한 줄씩 실행하는 단계

 

이 글은 각각의 과정을 최대한 단순화하여 정리해봅니다.

(세부적인 과정은 엔진마다 다르고, 훨씬 더 복잡할 것입니다.)

 

자바스크립트의 메모리 구조 및 콜스택/메모리힙에 대해서는 아래 글을 먼저 참고하시면 좋을듯 합니다.

[자바스크립트] 메모리 구조, 원시타입 변수 생성 원리, 가비지컬렉터

 

[자바스크립트] 콜스택/메모리힙 구조, 데이터 저장/참조 원리


 

먼저 아래 코드를 실행한다고 가정하자.

// 변수 선언문
var a = 10;
let b = 20;
const c = 30;

// 함수 선언문
function func_1(arg){
return arg;
}

// 함수 표현식(var)
var func_2 = function(arg){
return arg;
}

// 함수 표현식(const/let)
const func_3= function(arg){
return arg;
}

 


1Step: 생성 단계(Creation Phase)

자바스크립트는 소스코드를 실행하기 전!

소스코드 전체를 훑으면서 문제가 없는지 체크하는 과정을 거친다.

이 과정에서 실행컨텍스트를 생성하고, 변수 및 함수를 등록 해둔다

이 과정을 소스코드 평가 과정, 소스코드 스캔 과정이라고도 한다.

 

* 실행컨텍스트(Execution Context)란?

자바스크립트가 코드를 실행하기 위해 관리하는 Stack구조의 실행 환경이라고 볼 수 있습니다.

자바스크립트는 실행컨텍스트라는 구조를 통해 실행 시점의 변수, 함수, this, 스코프 등을 관리하며 명령어를 실행해 나갑니다.

 

* 렉시컬환경(Lexical Environment)이란? 

ECMAScript 명세서에서 "자바스크립트 엔진이 변수를 어떻게 관리해야 하는지" 기술하기 위해 사용된 명칭입니다.

실행컨텍스트의 컴포넌트로 변수 및 스코프, this 등을 관리하기 위한 추상적인 이론상의 개념이며, 구체적인 객체를 의미하는 것은 아닙니다.

자바스크립트 엔진들은 각기 다른 방식으로 ECMAScript의 렉시컬환경에 사항을 준수하면서 변수 환경을 관리합니다.

 

 

1단계(생성단계)에 일어나는 일을 단순하게 도식화해 보면 아래와 같다.

 

소스코드에서 변수/함수가 선언된 곳을 찾아서,

실행컨텍스트의 렉시컬 환경이라는 곳에 식별자(a, b, c, func_1, func_2, func_3)을 등록해둔다.

 

1. 변수

1) var

-  var a  코드를 발견하면,

- 실행컨텍스트의 렉시컬환경에 변수를 등록하고,

- 콜스택에 데이터 저장을 위한 메모리를 할당하고,

- 해당 콜스텍 메모리의 주소값을 변수에 저장하고,

- 확보된 콜스택의 메모리에는 ‘undefined를 할당(초기화) 해둔다.

- 따라서 var로 선언된 변수는 선언문 앞에서도 참조할 수 있게 되는데, 이것이 바로 호이스팅이 되는 원리다.

2) const/let

-  let b, const c  코드를 발견하면,

실행 컨텍스트의 렉시컬환경에 변수를 등록하고 끝난다.

따라서, 상태에서 변수를 참조하면  ReferenceError 발생한다.(Temorary Dead Zone 발생)

- 이 것이 let/const로 선언된 변수가 절반만 호이스팅된다고 말하는 이유다.

 

* var와 const/let 변수가 저장되는 위치

var로 선언된 변수와 let/const로 선언된 변수는 등록되는 공간이 서로 다릅니다.

전역에서 var로 선언된 변수는 렉시컬환경(Lexical Environment) 내부의 객체 환경 레코드(Object Environment Record)에 등록되고,

전역에서 const/let으로 선언된 변수는 렉시컬환경 내부의 선언적환경레코드(Declarative Envorinment Record) 라는 곳에 등록됩니다.

이로 인해 var로 선언된 변수는  전역객체 window.a 등으로 참조 가능하지만, let/const로 선언된 변수는 winodw.b 등으로 참조할 수 없게 됩니다.

 

2. 함수

1) 함수 선언문 (function으로 생성)

-  function func_1  코드를 발견하면,

- 실행컨텍스트의 렉시컬환경에 함수 식별자(func_1)를 등록해두고,

- 메모리 힙에 공간을 확보하고,

- 콜스택에 메모리 힙의 주소를 저장하고,

- 함수 변수(func_1)에 콜스택의 주소를 저장한 다음,

- 마지막으로 메모리힙에 함수 내용을 저장한다

- 따라서 완전 호이스팅되어 함수 선언문 앞에서도 함수를 호출할 수 있게 된다.

2) 함수 표현식 (var, let/const로 생성)

- 변수와 동일하게 작동한다.(var, let/const 에 따라 다름)

- var 선언된 함수에는 undefined 할당

- const/let으로 선언된 함수는 참조 불가능.


2Step: 실행 단계(Execution Phase)

실제 코드를 한줄씩 해석하며 실행해나가는 단계로 '런타임(Runtime)'이라고 볼 수 있다.

1. 변수

1) var

 a = 10;  코드를 만나면,

- 콜스택에 확보되어 있던 메모리(undefined) 말고, 새로운 메모리를 다시 확보하여 실제 값(10)을 저장하고,

- 변수 a에도 새로운 콜스택 주소값을 저장한다.

- 위의 그림에서도 a에 할당된 주소가 변경되었음을 확인할 수 있습니다.

2) let / const

-  let b = 20; const c = 30;  코드를 만나면

- 먼저  let b, const c  부분에서 메모리 확보하여 undefined 할당하고,

- undefined가 저장된 콜스택 메모리 주소값을 변수에 저장하고,

- 다음으로  b = 20; c = 30;  부분에서 다시 새로운 메모리를 확보해서 실제 값(b=20; c=30;)을 저장하고,

- 새로운 콜스택 메모리의 주소값을 변수 b, c에 다시 저장(교체)한다.

(그림은 그리기가 좀 복잡해서, 주소 변경은 생략되어 있습니다.)

 

2. 함수

1) 함수 선언문

- 1단계(생성단계)에서 이미 함수 식별자 및 구현부까지 메모리에 할당이 완료된 상태다.

2) 함수 표현식

- 변수와 동일하게 작동한다.(var, let/const 에 따라 다름)


최대한 단순화시켜 정리한다고 해보았습니다.

세부적인 과정은 엔진마다 다르고, 훨씬 더 복잡하다는 점을 참고하시기 바랍니다.

 

 

 

댓글