실행 컨텍스트란 자바스크립트 코드가 실행되고 연산되는 범위를 나타내는 추상적인 개념이다. 자바스크립트 엔진에 의해 만들어지고 사용되는 코드 정보를 담은 객체의 집합이라고 할수 있 다. 코드가 실행된다면 실행 컨텍스트 내부에서 실행되고 있는 것이다.
실행 컨텍스트 개념은 자바스크립트에서 호이스팅, 클로저, 스포프와 같은 개념을 이해하는데 매우 중요한 내용이다.
다른 프로그래밍 언어에서 불리는 호출스택(Calling Stack)은 실행스택과 같은 말이다. 실행 스택은 코드가 실행 중에 생성되는 모든 실행 컨텍스트를 저장하는데 사용하는데 사용되는 LIFO(Last in, First out)구조의 스택이다.
자바스크립트 엔진이 script
태그를 발견하면 전역 실행 컨텍스트(Global Execution Context)를 생성하고, 콜스택이라고 불리는 실행 컨텍스트 스택에 push
된다.
그리고 스크립트를 읽다가 함수가 실행되면 함수 실행 컨텍스트(Function Execution Context)가 생성되고, 함수의 실행 컨텍스트가 실행 컨텍스트 스택에 push
된다.
전역 컨텍스트는 코드를 실행하기 전에 먼저 쌓이는 것이고,
함수 컨텍스트는 함수를 실행할 때 생성된다.
실행 컨텍스트가 생성될 수 있는 방법은 전역공간
, 함수
, eval() 함수
가 있다.
자바스크립트 코드는 3가지 종류로 이루어 지는데, 글로벌 스코프에서 실행하는 전역공간, 함수 스코프에서 실행하는 함수 코드, 그리고 eval()함수가 있다. 이 각각의 코드는 자신만의 하나의 실행 컨텍스트를 생성한다.
전역 실행 컨텍스트(Global Execution Context) : 가장 기본 베이스가 되는 실행 컨텍스트이다. 특정 함수 안에서 실행되는 코드가 아니라면 코드는 전역 컨텍스트에서 실행된다. 다시 말해서 모든 스크립트의 코드는 전역 실행 컨텍스트안에서 실행된다. 브라우저의 경우에는 window
객체, Node.js의 경우 global
객체가 곧 전역 실행 컨텍스트가 된다.
함수 실행 컨텍스트 (Function Execution Context) : 함수가 호출 될 때마다 해당 함수에 대한 새로운 실행 컨텍스트가 생성된다. 각 함수들은 자신만의 실행 컨텍스트를 가지는데 실행 컨텍스트는 함수가 호출이 되어야 생성이 된다.
Eval Function Execution Context : eval() 함수 또한 자신만의 실행 컨텍스트를 가진다. 자바스크립트에서 많이 쓰이지 않아 신경쓰지 않아도 된다.
실행 컨텍스트는 다음 3가지 구조를 가진다.
1. Lexical Environment
2. Variable Environment
3. this Binding
변수 및 함수 등의 식별자 및 외부 참조에 관한 정부를 가지고 있는 컴포넌트 이다. 이 컴포넌트는 2개의 구성요소를 갖는다. 1) Environment Record, 2) outer 참조
관련 어휘 환경 범위 내에서 식별자를 해당 값에 매칭하는 구조이다. let
Environment Record가 식별자에 관한 정보를 가지고 있으며, outer 참조는 외부 Lexical Environment를 참조하는 포인터이다.
자바스크립트 엔진이 실행 컨텍스트를 만들때 두가지 과정을 거친다.
1) 생성 단계 Creation Phase
2) 실행 단계 Execution Phase
실행 컨텍스트는 생성 단계에서 생성된다. 이때 두가지 일이 일어 난다.
1. Lexical Environment 컴포넌트가 생성
2. Variable Environment 컴포넌트가 생성
ExecutionContext = {
LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
VariableEnvironment = <ref. to VariableEnvironment in memory>
}
간단히 말해서 Lexical Environment는 식별자(identifier)-변수 매핑(variable mapping)이 되는 곳이다.
참조 대상 식별자인 identifier는 함수와 변수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별 할 수 있는 유일한 이름이다. 자바스크립트는 이 규칙대로 식별자를 찾는다.
다시 말해서 Lexical Environment에서는 변수와 해당 변수에 대입된 값이 매핑되는 곳이라고 할수 있다.
var a=20;
var b=30;
function foo() {
console.log('bar');
}
위 코드의 Lexical Environment는 다음과 같다.
lexicalEnvironment = {
a: 20,
b: 30,
foo: <ref. to foo function>
Lexical Environment는 다음 3가지 구성요소가 있다.
Environment Record
Environment Record는 Lexical Environment내에서 변수 및 함수 선언이 저장되는 장소이다.
이는 2가지 유형의 Environment Record가 있다.
outer 참조
외부 환경으로의 참조 값의 의미는 외부 lexical환경으로 접근 할 수 있다는 의미이다. 자바스크립트 엔진이 현재의 lexical environment에서 변수를 찾지 못했다면 외부 환경에서 해당 변수를 찾아 볼수 있다는 의미이다.
this binding
this
는 여기서 결정된다.
전역 실행 컨텍스트에서 this
는 global object이다. (브라우저에서는 this
가 window
를 가리킴)
함수 실행 컨텍스트에서는 this
는 함수가 어떻게 호출되는 방식에 따라 다르다. 함수가 object reference로 호출된다면 this
는 해당 객체를 가리키게 되고, 그렇지 않다면 this
는 글로벌 객체 window
를 가리키거나 strict mode에서는 undefined
를 가리킨다.
Variable Environment는 Lexical Environment와 동일한 성격을 띠지만 var
로 선언된 변수만 저장한다는 점에서 다르다. 즉, Lexical Environment는 var
로 선언된 변수를 제외하고 나머지(let
와 const
으로 선언되었거나 함수 선언문)를 저장한다.
자바스크립트 엔진이 코드 한줄 한줄 읽으면서 코드를 실행하는 단계이다. 실행 단계는 모든 변수에 값이 할당된 상태이다.
생성 단계(Creation Phase)에서 즉 실행 컨텍스트가 만들어 질때, let
과 const
로 정의된 변수에는 할당된 값이 없지만 var
로 정의된 변수는 undefined을 가지게 된다.
이는 생성 단계에서 실행 컨텍스트가 만들어지는 동안 인터프리터는 변수와 함수 선언을 위해 스캔을 한다. 이때 함수의 선언은 환경(Environment)에 저장되고, 변수는 기본 값으로 undefined
나 아직 초기화가 되지 않은 상태로 저장된다. (이것이 호이스팅 개념이다.)
따라서 var
는 변수가 선언되기 전에 undefined라는 값으로 접근 할 수 가 있고, let
과 const
는 선언되기 전에 접근하면 reference error를 뱉는다.
우리는 이것을 호이스팅이라고 부른다.
그리고 실행단계 (Execution Phase)에서 자바스크립트 엔진이 코드상에서 let
으로 선언된 변수의 값이 선언된 곳을 찾을 수 없으면 undefined
을 할당한다.
위 복잡한 개념들을 한번 정리해 보면...
자바스크립트는 단일 스레드 언어이기 때문에 한번에 하나의 작업만 실행 할 수 있다. 자바스크립트 인터프리터가 처음에 코드를 실행 할 때 기본적으로 먼저 Global Execution Context를 생성한다. 이 시점부터 함수를 호출할 때마다 새로운 실행 컨텍스트가 생성된다.
새 실행 컨택스트가 생성 될때마다 콜스택 맨 위에 추가된다. 브라우저는 항상 콜스택 맨 위에있는 현재 실행 컨텍스트를 실행한다. 이것이 완료되면 스택에서 제거가 되고, 그 다음 바로 아래에 있는 실행 컨텍스트로 돌아가 실행을 이어간다.
실행 컨텍스트는 생성 단계와 실행 단계 둘로 나눌 수 있다.
생성단게에서 인터프리터는 실행 컨텍스트 내에서 정의된 모든 변수, 함수 및 인수로 구성된 변수 객체를 먼저 생성한다.
그 다음, 범위 체인이 초기화가 되고, this의 값이 마지막으로 결정된다. 그리고 모든 변수에 값이 할당된 상테에서 실행 단계에서 코드가 해석되고 실행이 된다.
참고