이 글은 'Deep Dive'를 보고 정리한 글이다. 실행 컨텍스트를 직접 설명하려니 글이 너무 안 써져서 책의 표현을 많이 빌려 썼다. 문제가 되면 글을 삭제하도록 하겠다. 링크 걸어놓은 사이트를 가면 훨씬 설명이 잘 되어 있으니 참고하길 바란다.
실행 컨텍스트란?
- 실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역이다.
- 실행 컨텍스트는 식별자(변수, 함수, 클래스 등의 이름)를 등록하고 관리하는 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.
말이 좀 어렵지만 정말 쉽게 말하면 실행 컨텍스트는 소스코드 내의 모든 변수와 함수의 참조 관계를 도식화한 지도로 생각하면 된다.
자바스크립트 엔진은 소스코드 실행에 앞서 소스코드의 '평가'와 소스코드의 '실행'으로 나누어 처리한다.
평가 과정
: 실행 컨텍스트를 생성하고 변수, 함수 등의 선언문만 먼저 실행하여 식별자들을 스코프에 등록한다.
실행 과정
: 선언문을 제외한 소스코드가 순차적으로 실행된다. 이때 소스코드 실행에 필요한 정보, 즉 변수나 참조를 실행 컨텍스트가 관리하는 스코프에서 검색해서 취득한다.
모든 소스코드는 '전역 실행 컨텍스트'가 먼저 생성된다. 즉, 자바스크립트 엔진은 모든 소스코드를 실행하기 전에 '전역 코드 평가'를 통해 '전역 실행 컨텍스트'를 생성하고, 이후에 전역 코드를 실행한다. 코드를 실행하면서 새로운 컨텍스트가 생성되는 시점은 함수가 호출될때다.
함수가 호출되면 기존의 컨텍스트는 일시 중단하고 코드의 제어권이 호출된 함수 내부로 이동한다. 자바스크립트 엔진은 함수 내부의 함수 코드를 평가하여 '함수 실행 컨텍스트'를 생성하고, 실행 컨텍스트 스택에 push 한다. 이후 함수 코드가 순차적으로 실행된다. 함수 실행이 완료되면 함수 실행 컨텍스트는 스택에서 pop되어 소멸한다.
이렇듯 실행 컨텍스트는 함수 레벨 스코프 단위로 생성된다. 그런데 함수는 렉시컬 스코프 규칙을 따르므로 함수 실행 컨텍스트의 스코프는 함수 호출 시점이 아닌 함수 선언 시점이 된다. 스코프 내의 식별자들은 실행 컨텍스트의 렉시컬 환경으로 관리한다.
말이 좀 길었는데 위 글들을 한문장으로 정리하면 아래와 같다.
식별자와 스코프는 실행 컨텍스트의 렉시컬 환경으로 관리하고, 코드 실행 순서는 실행 컨텍스트 스택으로 관리한다.
직접 해보기
const x = 1;
function foo() {
const y = 2;
function bar() {
const z = 3;
console.log(x + y + z);
}
bar();
}
foo(); // 6
위 코드의 동작을 실행 컨텍스트 관점에서 보자. 우선 간단하게만 살펴보자.
처음에 전역 실행 컨텍스트가 생성되어 스택에 push될 것이다. 코드 실행중에 foo() 를 만나면 foo 함수의 실행 컨텍스트가 생성되어 스택에 push될 것이다.
foo 함수 내부에서 bar()를 만나면 bar 함수의 실행 컨텍스트가 생성되어 스택에 push될 것이다.
이후에 bar 함수 실행이 끝나면 bar 함수 실행 컨텍스트는 스택에서 pop되고 차례대로 foo 함수 실행 컨텍스트와 전역 실행 컨텍스트 또한 스택에서 pop 되면서 소스코드 실행이 종료된다.
소스코드 동작 순서는 위와 같다. 이제 그림으로 상세하게 살펴보자.
※ 아래 그림에 표시되지 않았지만 GlobalEnvironmentRecord는 전역 객체에 바인딩된다. foo와 bar 실행 컨텍스트의 this 또한 전역 객체에 바인딩 된다(그림에서 this 바인딩 잘못됨)
1) Global Execution Context 생성 및 실행
2) foo Execution Context 생성 및 실행
3) boo Execution Context 생성 및 실행
위 그림에서 아래 코드가 어떻게 실행되는지 살펴보자.
console.log(x + y + z);
- 먼저 현재 실행중인 컨텍스트의 렉시컬 환경에서 console 식별자를 검색한다.
- bar 렉시컬 환경에는 console이 없으므로 상위 스코프인 foo 렉시컬 환경에서 console 식별자를 검색한다.
- foo 렉시컬 환경에는 console이 없으므로 상위 스코프인 global 렉시컬 환경에서 console 식별자를 검색한다.
- console 객체의 프로토타입 체인을 통해 log 메서드를 검색한다.
- x는 스코프 체인을 통해 global 렉시컬 환경의 값을 참조한다.
- y는 스코프 체인을 통해 foo 렉시컬 환경의 값을 참조한다.
- z는 boo 렉시컬 환경에 있으므로 그대로 쓴다.
- 위 코드를 실행하고 bar 함수를 종료한다.
이후에 실행 컨텍스트는 스택에서 하나씩 제거되면서 소스코드는 실행 완료된다.
추가로 자바스크립트 엔진은 실행 컨텍스트를 통해 메모리 관리를 한다고 한다. 실행 컨텍스트는 모든 스코프 체이닝을 통해 모든 변수간의 참조 관계를 알 수 있다. 만약에 참조되는 값이 없는 변수가 존재하면 garbage collection이 자동으로 해당 변수를 할당 해제한다고 한다.
'JavaScript' 카테고리의 다른 글
[ JavaScript ] Promise (0) | 2022.04.27 |
---|---|
[ JavaScript ] 함수와 클로저(Closure) ... 커링을 곁들인 (0) | 2022.04.23 |
[ JavaScript ] 스코프와 호이스팅(feat. var let const) (1) | 2022.04.22 |
[ JavaScript ] this (0) | 2022.04.19 |
[ JavaScript ] String Methods & 정규표현식 (0) | 2022.04.17 |