방춘덕(고양이 키우면 지을 이름)의 개발 블로그입니다.
execution context의 생성과정 본문
지난 글에서는 EC(execution context)와 ECS(execution context stack)이 무엇인지 알아봤고 오늘은 실제로 EC가 어떻게 생성되는지 알아볼 것이다. (이 글을 이해하기 위해서는 꼭 지난 글을 읽어야만 한다.)
JS엔진은 2가지 단계로 EC를 생성한다.
1). creation phase
생성 단계는 JS 엔진이 함수를 호출했지만, 아직 실행이 되지 않은 상태다. 이 단계에서 JS 엔진은 컴파일 단계에 있으며 작성된 코드를 컴파일하기 위해 함수를 스캔한다.
global EC일 때와 function EC일 때 동작이 아주 약간 다르다. 그리고 이전 글의 각 EC별 특징과 매우 흡사하다.
- global EC
- 전역 객체를 생성한다. (window... 등)
- this 객체를 생성한다.
- 함수와 변수들을 위한 메모리 공간을 할당한다.
- 함수 및 변수들을 메모리 공간에 할당이 완료될 때 까지는 undefined로 선언한다.
- function EC
- 매개 변수 객체를 생성한다.
- this 객체를 생성한다.
- 함수와 변수들을 위해 메모리 공간을 할당한다.
- 함수 및 변수들을 메모리 공간에 할당이 완료될 때 까지는 undefined로 선언한다.
그리고 위의 내용은 lexical environment와 variable environment라는 컴포넌트를 통해 구현된다. 기본적으로 두 컴포넌트 모두 lexical environment에 대한 참조이며 처음에는 같은 lexical environment를 참조한다.
1-1). VE (variable environment)
execution context의 변수 및 함수의 초기 저장 영역이다. context가 함수에 처음 진입할 때 값이 채워지며, 함수의 매개변수, 호출한 함수, outer 등을 저장하고 있다.
function print(text) {
console.log(text)
}
print('YEE')
대략 위와 같은 코드는 context 생성 시 아래와 같이 생성된다.
printContext.VariableEnvironment = {
environmentRecord: {
arguments: {0: 'YEE', length: 1, callee: print},
text: 10
},
outer: globalEnvironment
}
1-2). LE (lexical environment)
LE는 자바스크립트 코드에서 변수, 함수를 정의하는 데 사용되는 객체라고 이해하면 된다. LE는 environment record와 outer라는 또 다른 LE로 구성되어 있다. 사실상, 함수 실행 초기에는 그저 VE를 복사해온 객체다.
function mart() {
var emart = '이마트'
var lottemart = '롯데마트'
var homepluse = '홈플러스'
function production() { ... }
}
mart()
위의 예시 코드는 간단하게 아래와 같은 형태의 LE로 표현이 가능하다.
{
environmentRecord: {
emart: '이마트',
lottemart: '롯데마트',
homeplus: '홈플러스',
production: <Function>
}
outer: mart.[[Environment]]
}
위의 LE는 아주 간단한 예시로써 실제로는 BlockStatement, catch, with등에 의해 더 다양한 형태를 띠고 상황에 따라 삭제와 생성이 진행된다.
1-2-1). ER (environment record)
LE안의 함수 및 변수들의 바인딩을 기록하는 역할을 하고 있다. 크게는 declarative environment record와 object environment record가 있으며 세부적으로는 global environment record, function environment record, module environment record가 존재한다.
이에 관해 간략하게 설명해보자면
- declarative ER
- 변수 선언, 함수 선언 부등을 저장하고 있다.
- object ER
- property의 이름들과 일치하는 identifier들을 연결한다.
- 객체들의 property를 바인딩하기 위해 record에 새로운 entry를 생성한다.
- global ER
- object ER, declarative ER로 이루어져 있다.
- object ER은 일반적인 ER처럼 동작하지만, 바인딩을 객체와 동기적으로 유지하면 이 경우 객체는 전역 객체다.
- 일반적인 declarative ER처럼 동작한다.
1-3). outer environment record
outer는 이전 글에서 SC (scope chain)을 위해 존재하는 녀석이다.
ECMAScript 3판까지는 Scope Chain 이란 용어를 직접 명시했다면, ES5부터는 Lexical nesting structure 또는 Logical nesting of Lexical Environment values 등으로 표현하고 있다.
var value = 'VALUE'
function A() {
var a = 'a'
function B() {
var b = 'b'
console.log(value) // 'VALUE'
console.log(a) // 'a'
console.log(b) // 'b'
}
B()
}
A()
위와 같은 코드가 있다면 LE로 아래와 같이 번역될 것이다.
GlobalEnvironment = {
environmentRecord: {
value: 'VALUE'
},
outer: null
};
AEnvironment = {
environmentRecord: {
a: 'A'
},
outer: globalEnvironment
}
BEnvironment = {
environmentRecord: {
b: 'B'
},
outer: AEnvironment
}
코드를 보면 알 수 있다시피, B -> A -> global 순서로 outer가 참조되고 있다는 것을 알 수 있다. 즉, outer는 자신을 호출한 함수에 대한 LE라고 볼 수 있다. 이로 인해 상위 execution context에서 변수를 가져올 수 있는 것이다.
2). execution phase
이제 execution context가 생성되었으니, 코드를 읽어내려가며 할당만 해놓고 초기화하지 않았던 변수들에 대해서 초기화를 진행한다.
그리고 코드를 실행하며 함수를 호출하게 된다면 다시 creation phase로 돌아가 생성 -> 실행... 을 반복한 뒤 실행이 완료 되었다면 execution context stack에서 현재 execution context를 pop한 뒤 이전 execution context로 컨트롤을 넘긴다.
마무리하며..
아래 사이트는 작성한 JS 코드를 단계별로 (혹은 자동으로) execution context가 생성되고 실행되는 모습을 보여준다. 위의 내용이 잘 이해가 가지 않았다면 (솔직히 글을 잘 못쓰긴 한다.) 아래 링크와 블로그들을 통해서 다시 한번 학습해보는 것을 추천한다.
이 글을 작성할 때 읽은 감사한 자료들.
https://poiemaweb.com/js-execution-context
https://hackernoon.com/execution-context-in-javascript-319dd72e8e2c
https://meetup.toast.com/posts/129
'JS' 카테고리의 다른 글
prototype과 prototype chain (0) | 2019.12.16 |
---|---|
scope (1) | 2019.12.13 |
event loop (0) | 2019.12.11 |
hoisting (0) | 2019.12.07 |
execution context 와 execution context stack (0) | 2019.12.02 |