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

  1. 실행할 코드에 제공할 환경 정보들을 모아 놓은 객체이다.
    • 전역 코드 : 전역 영역에 존재하는 코드
    • 함수 코드 : 함수 영역에 존재하는 코드
    일반적으로 실행할 코드는 전역 코드와 함수 내 코드이다.
  2. 자바스크립트 엔진이 코드를 실행하기 위해 필요한 모든 정보를 하나의 객체로 묶어 놓은 것이다.

실행 컨텍스트 생성 과정

1. 실행 컨텍스트 생성

2. 스코프 체인의 생성과 초기화

실행 컨텍스트가 생성된 이후 가장 먼저 스코프 체인의 생성과 초기화가 실행된다.

이때 스코프 체인은 전역 객체의 레퍼런스를 포함하는 리스트가 된다.

3. 변수 객체화 실행

스코프 체인의 생성과 초기화가 종료하면 변수 객체화가 실행된다.

변수 객체화는 변수 객체에 변수, 매개변수와 인수 정보(arguments), 함수 선언을 추가하는 것을 의미한다.

전역 코드의 경우 변수 객체는 전역 객체(GO; Global Object) 를 가리킨다.

함수의 경우 변수 객체는 활성화 객체(AO; Activation Object) 를 가리킨다.

 

변수 객체화는 아래의 순서로 변수 객체에 프로퍼티와 값을 설정한다.

  1. (함수 코드인 경우) 매개변수(parameter) 가 변수 객체의 프로퍼티로, 인수(arguments) 가 값으로 설정된다.
  2. 대상 코드 내의 함수 선언을 대상으로 함수명이 변수 객체의 프로퍼티로, 생성된 함수 객체가 값으로 설정된다. (함수 호이스팅)
  3. 대상 코드 내의 변수 선언을 대상으로 변수명이 변수 객체의 프로퍼티로, undefined 가 값으로 설정된다.(var 로 선언된 변수의 경우에만) (변수 호이스팅)

실행 컨텍스트(Execution Context) 의 종류

실행 컨텍스트의 종류는 아래의 세 가지로 나누어진다.

1. Global Execution Context :

코드를 실행하며 단 한 개만 정의되는 전역 컨텍스트이다.

전역 객체(global object) 를 생성하며 this 값에 전역 객체(global object) 를 참조한다.

콜스택에 가장 먼저 push(추가)되며 앱이 종료될 때 pop(제거)된다.

2. Functional Execution Context:

함수가 실행 될 때마다 정의되는 컨텍스트이다.

매 실행 시마다 정의되며 함수 실행이 종료(return)되면 콜스택에서 pop(제거)된다.

3. Eval Context

eval 함수로 실행한 코드의 컨텍스트이다.

 

실행 컨텍스트(Execution Context)의 관리 : 콜스택(CallStack)

자바스크립트 엔진은 생성된 실행 컨텍스트를 관리하기 위해 단 하나의 콜스택 을 가지고 있다.

  • 자바스크립트 엔진은 전역 범위의 코드를 실행하며 Global Execution Context 를 생성해 콜스택에 push(추가) 한다.
  • 함수가 실행 또는 종료될 때마다 Global Execution Context 의 위로 Functional Execution Context 를 콜스택에 push(추가), pop(제거)한다.

예시)

let name = 'Ron';

function fn1() {
    fn2();
}

function fn2() {
    console.log(`My name is ${name}`);
}

fn1();
  1. 코드의 전역 범위가 실행되며 Global Execution Context 를 push(추가)한다.
  2. fn1 이 실행된다.
  3. fn1 의 Functional Execution Context 가 콜스택에 push(추가)된다.
  4. fn2 가 실행된다.
  5. fn2 의 Functional Execution Context 가 콜스택에 push(추가)된다.
  6. console.log 가 실행된다.
  7. console.log 의 Functional Execution Context 가 콜스택에 push(추가)된다.
  8. console.log 의 실행이 완료되며 console.log 의 Functional Execution Context 가 pop 된다.
  9. fn2 의 실행이 완료되며 fn2 의 Functional Execution Context 가 pop 된다.
  10. fn1 의 실행이 완료되며 fn1 의 Functional Execution Context 가 pop 된다.
  11. 프로그램 종료 시 Global Execution Context 가 pop 된다.

실행 컨텍스트(Execution Context) 의 구성

ExecutionContext: {
    LexicalEnvironment: {
        Environment Records,
        Reference to the outer environment 
    },
    VariableEnvironment: {
        Environment Records,
        Reference to the outer environment
    }
}

ExecutionContext 는 LexicalEnvironment와 VariableEnvironment 의 두 가지 속성으로 이루어지며, 각 Environment 들은 생성 시 같은 속성들을 가지고 있다.

1. 외부 환경 참조(Reference to the output environment)

외부 환경 참조는 렉시컬 스코프(Lexical Scope)를 기준으로 상위 스코프의 렉시컬 환경(Lexical Environment) 를 참조한다.

가장 먼저 생성되는 Global Execution Context 는 외부 환경 참조 값으로 null 을 가진다.

Functional Execution Context 는 상위 스코프에 해당하는 렉시컬 환경(Lexical Environment) 을 외부 환경 참조 값으로 가진다.

이 연결 고리는 변수 탐색 시 사용된다.

 

scope chain

let name = "Ron";

function fn1() {
    console.log(name); // Ron
}

function fn2() {
    let name = "Byungchan"; 
    console.log(name);
    fn1();
}

fn2();
Global Lexical Environment: {
    ...,
    외부 환경 참조: null
}
fn1 Lexical Environment: {
    ...,
    외부 환경 참조: Global Lexical Environment
}
fn1 inner console.log Lexical Environment: {
    ...,
    외부 환경 참조: fn1 Lexical Environment
}
fn2 Lexical Environment: {
    ...,
    외부 환경 참조: Global Lexical Environment
}
fn2 inner console.log Lexical Environment: {
    ...,
    외부 환경 참조: fn2 Lexical Environment
}

fn1 내부의 console.log 는 name 변수 탐색 시 가장 먼저 자신의 Lexical Environment 확인. 그리고 name 변수를 찾지 못했을 경우 자신의 외부 환경 참조인 fn1 의 Lexical Environment 를 탐색하기 시작한다.

→ fn1 에서도 name 을 찾지 못했기 때문에 fn1 의 외부 환경 참조인 Global Lexical Environment 에서 탐색을 이어가고 결국 원하는 값을 찾게 된다.

→ 이 참조 연결 고리는 목적인 변수를 찾아내거나 Global Lexical Environment 에 다다를 때까지 이어진다.

자바스크립트에서 스코프 결정 시 렉시컬 스코프(정적인 스코프) 를 따르므로 외부 환경 참조 값의 결정은 함수가 호출된 위치가 아닌 함수가 선언된 위치에 따라 결정된다.

 

2. Environment Record

Environment Record 는 Lexical Environment 내에 식별자 바인딩을 기록하는 객체이다.

 

Environment Record 의 구성

Environment Record: {
    Declarative Environment Record,
    Object Environment Record,
    Global Environment Record
}

이 중 Declarative Environment Record 에 함수와 변수, this, super 등의 식별자 바인딩이 저장된다.

Variable Environment 와 Lexical Environment 는 각각 다른 방식으로 선언된 변수들을 관리한다.

Variable Environment 에는

  • var 로 선언된 변수가 메모리에 매핑되며 초기값으로 undefined 가 할당된다.
  • 변수 값 할당 코드가 실행되기 전 변수에 접근하게 되면 undefined 값을 얻게 되며, 할당 코드가 실행되고 난 뒤에는 해당 값으로 수정된다.
  • 선언형 함수가 메모리에 매핑되며 함수 전체가 메모리에 할당된다.

Lexical Environment 에는

  • let, const 로 선언된 변수가 메모리에 매핑되지만 초기값은 할당되지 않는다. 변수 값 할당 코드가 실행되기 전 변수에 접근하게 되면 reference error 가 발생한다. 초기값 할당 코드가 실행되고 난 뒤에 메모리에 값이 추가된다.

실행 컨텍스트(Execution Context) 의 생성 과정

실행 컨텍스트는 Creation 과 Execution 의 두 단계를 거쳐 생성된다.

1. 생성 단계 (Creation Phase)

Lexical Environment 와 Variable Environment 의 정의가 이루어진다.

this 바인딩과 외부 환경 참조 값을 결정한다.

Environment Record 에 변수 식별자에 대한 메모리가 매핑되며 값의 할당은 선언 방식에 따라 다르게 이루어진다.

  • Lexical Environment 에는 let, const 로 선언된 변수가 메모리에 매핑되지만 코드 실행 시점에 실제 코드 선언부를 만나기 전까지 초기값은 할당되지 않는다.
  • Variable Environment 에는 var 로 선언된 변수가 메모리에 매핑되며 초기값으로 undefined 가 할당된다. 또한 선언형 함수가 메모리에 매핑되며 함수 전체가 메모리에 할당된다.

2. 실행단계(Execution Phase)

생성 단계에서 코드 실행을 위한 환경 정보 값이 결정되었다면 코드를 위에서부터 읽으며 실행한다.

변수 값이 할당되는 코드가 실행될 경우 Environment Record 에 저장된 식별자 메모리에 값을 수정 또는 할당한다.

 

코드를 통해 Lexical Environment 와 Variable Environment 설정 과정 알아보기

function sayHelloOneTime() {
    var isMorning = true;
    let hello = "Good morning!";
    while (isMorning) {
        var name = "Ron";
        let question = "How are you?";
        console.log(`${name} ${hello} ${question}`);
        isMorning = false;
    }
}
sayHelloOneTime();

sayHelloOneTime 은 호출되며 Execution Context 를 생성한다.

Variable Environment 는 var 가 유효한 함수 스코프를 범위로 가지고 있기 때문에 sayHelloOneTime 의 스코프 내에 있는 var 로 선언된 isMorning 과 name 의 식별자 정보를 매핑한다.

Lexical Environment 는 let, const 가 유효한 블록 스코프를 범위로 가지고 있기 때문에 시작은 Variable Environment 와 같이 sayHelloOneTime 의 블록을 범위로 가진다. 또 Environment Record 에 let 으로 선언된 hello 의 정보를 매핑하고 있다.

그리고 sayHelloOneTime 에는 while 블록이 있다. while 블록은 다시 Lexical Environment 를 정의하며, Environment Record 에 let 으로 선언된 question 의 정보를 매핑한다. 또 외부 환경 참조 값으로 sayHelloOneTime 의 Lexical Environment 를 가진다.

 

참조

복사했습니다!