실행 컨텍스트란 실행할 코드의 환경 정보들을 모아놓은 객체이다. 실행 컨텍스트는 보통 함수를 실행시킴으로써 구성할 수 있는데, 구성된 실행 컨택스트는 콜 스택에 쌓이게 되고 가장 위에 있는 컨텍스트와 관련있는 코드를 실행하여 전체 코드의 환경 및 순서를 보장한다.
아래 예시를 보며 실행 콘텍스트가 생성되고 콜 스택에 추가, 삭제되는 과정을 살펴보자.
[1] var a = 1;
[2] function outer(){
[3] function inner(){
[4] console.log(3);
[5] var a = 3;
[6] }
[7] inner();
[8] console.log(a);
[9] }
[10] outer();
[11] console.log(a);
- 코드가 실행되며 전역 컨텍스트 활성화
- [10]번 줄에서 outer함수를 호출하며 outer 실행 컨텍스트 생성 및 콜 스택에 추가. 이때 전역 컨텍스트는 일시 중지되며 outer실행 컨텍스트와 관련된 코드 실행.
- [7]번 줄에서 inner함수를 호출하며 inner 실행 컨텍스트 생성하여 콜 스택에 추가. outer 실행 컨텍스트 일시중지 및 inner컨텍스트와 관련된 코드 순차적으로 실행.
- inner 함수 실행 종료 후 콜 스택에서 inner 실행 콘텍스트 제거
- 일시 중단 되었던 outer 실행 콘텍스트가 실행되며 [8]번 줄 코드 실행 후 outer함수 실행 종료로 콜 스택에서 outer 실행 콘텍스트 제거
- [11]번째 줄 실행 후 전역 컨텍스트도 제거
이렇게 실행 콘텍스트는 함수 호출, 전역 공간을 통해 생성되며 콜 스택 가장 상단에 위치한 실행 콘텍스트와 관련된 코드가 모두 실행이 되면 콜 스택에서 제거된다. 이로써 동일한 환경 내의 코드를 실행시킬 때의 환경 정보들을 활용하며 코드의 실행 순서도 관리할 수가 있다.
실행 컨텍스트에 담기는 정보는 VariableEnviroment, LexicalEnviroment, ThisBinding이 있다.
VariableEnviroment는 최초 실행 시의 식별자 정보를 가지고 있다. 다만 변경 사항들이 반영되지 않으며 마치 초기 환경 정보들을 사진을 찍어 놓은 상태처럼 가지고 있다.
LexicalEnviroment는 VariableEnviroment를 복제하여 생성이 되며 코드 진행에 따라 생기는 변경 사항들이 반영이 된다. LexicalEnviroment는 enviromentRecord, outerEnviromentreference로 이루어져 있다.
현재 실행 컨텍스트와 관련된 내부 식별자 정보들이다. 매개변수명, 함수, var로 선언된 변수 식별자와 같은 정보들이 이에 해당한다. 컨텍스트 내부를 위부터 훑으며 순서대로 수집해나간다. 따라서 자바스크립트 엔진은 코드가 실행되기 전에도 해당 환경에 존재하는 함수 및 변수명을 이미 알고있게 된다. 이렇게 식별자 정보들을 수집하면서 마치 변수 선언부를 코드 가장 상단으로 끌어올린 것처럼 보이는 현상을 호이스팅(hoisting)이라고 한다.
outerEnviromentReference는 현재 환경과 관련있는 외부 식별자 정보이다. 현재 컨텍스트 상위의 컨텍스트의 LexicalEnviroment를 참조한다.
a라는 함수 내부에 함수 b를 선언하고 함수 b내에 함수 c를 선언한 경우 c의 outerEnviromentReference는 함수 b의 LexicalEnviroment를 참조하고 함수 b의 outerEnviromentReference는 함수 a의 LexicalEnviroment를 참조한다. 따라서 함수 b 내부에 선언한 변수를 함수 c 내에서 활용할 경우 함수 c의 enviromentRecord에서 해당 변수를 먼저 찾은 뒤, 없으면 함수 b의 LexicalEnviroment를 참조하고 있는 enviromentRecord에서 변수를 찾게 되는 것이다. 마치 연결리스트의 형태와 같은 enviromentRecord의 연결고리를 계속 참조하다보면 전역 컨텍스트의 LexicalEnviroment까지 만날 수 있게 된다.
아래 예시 코드를 보며 이해해보자.
[1] var a = 1;
[2] var outer = function(){
[3] var inner = function(){
[4] console.log(a);
[5] var a = 3;
[6] };
[7] inner();
[8] console.log(a);
[9] };
[10] outer();
[11] console.log(a);
위 코드는 다음과 같은 흐름으로 실행될 것이다.
- 전역 컨텍스트 활성화,
enrivomentRecord에 {a, outer} 저장. (전역 컨텍스트는 선언 시점이 없으므로outerEnviromentReference에는 아무것도 담기지 않음.)
- 10번에서 outer함수 호출로 전역 컨텍스트가 일시중단되며 outer실행 컨텍스트 생성.
enrivomentRecord에는 {inner}가 저장되며outerEnviromentReference에는 전역 컨텍스트의LexicalEnviroment를 [GLOBAL, {a, outer}]와 같이 참조복사함. (GLOBAL은 실행 컨텍스트 이름, {}는enviromentRecord객체)
- 7번째 줄에서 inner함수를 호출하며 outer 실행 컨텍스트 중단 및 inner실행 컨텍스트 생성.
enrivomentRecord에는 {a}가 저장되며outerEnviromentReference에는 outer 내부에서 선언되었으므로 outer 함수의LexicalEnviroment를 [outer, {inner}] 와 같이 참조 복사
- 4번째 줄을 실행하기 위한 변수 a를 inner
enviromentRecord에서 찾은 후 undefined 출력. (아직 초기화 구문을 실행하지 않았으므로)
- 8번째 줄을 실행하기 위한 변수 a를 outer의
enviromentRecord에서 찾은 후 없을 시outerEnviromentReference의enviromentRecord조회. 전역 컨텍스트의LexicalEnviroment에 있는 변수 a를 찾아 출력
- 11번에서 전역 컨텍스트의
LexicalEnviroment에 있는 변수 a를 찾아 출력.
이처럼 outerEnviromentReference를 통해 상위 스코프를 참조할 수 있는 스코프 체인이 형성되어 외부 식별자 정보를 참조할 수 있다.
실행 컨텍스트는 호이스팅, 클로저, 스코프 등과 관련된 자바스크립트의 핵심 개념이므로 이를 이해하고 넘어가면 자바스크립트의 동작을 이해하는 데에 많은 도움이 될 것이다.
참고: 코어자바스크립트