도리쓰에러쓰

[JavaScript] 실행 컨텍스트(Execution Context)란? 본문

JavaScript/JS

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

강도리 2022. 8. 10. 15:48

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

자바스크립트 엔진은 코드를 실행하기 위해 실행에 필요한 여러가지 정보(전역변수, 매개변수, 유효범위, this 등)들을 알고 있어야 한다. 어떤 실행 컨텍스트가 활성화될 때, 자바스크립트 엔진은 해당 컨텍스트의 코드를 실행하는데 필요한 환경 정보들을 수집해서 실행 컨텍스트에 저장한다.

다시 말해, 코드가 실행되기 위해 필요한 정보들을 가진 범위를 추상화하기 위해 객체 형태로 나타낸 것이다.

 

자바스크립트에서 실행 컨텍스트(Execution Context)를 만들 수 있는 방법은 총 4가지이다.

1️⃣ 전역 코드: 전역 영역에 존재하는 코드

2️⃣ Eval 코드: eval() 함수로 실행되는 코드

3️⃣ 함수 코드: 함수 내에 존재하는 코드

4️⃣ (ES6부터) 블록문

 

실행 컨텍스트(Execution Context)는 논리적 스택 구조를 가진다.

가장 위에 쌓여있는 컨텍스트와 관련 있는 코드를 실행하는 식이다.

즉, LIFO(Last In First Out)의 구조를 가지고 있고 이 순서대로 진행되는 것이다.

아래 예시를 보자.

let a = 'a';

const example1 = () => {
  let b = 'b';
  
  const example2 = () => {
    let c = 'c';
    console.log(x + y + z);
  }
  
  example2();
}

example1();

 

위 코드를 실행하면 아래 사진과 같이 실행 컨텍스트(Execution Context)가 쌓이고 소멸하게 된다.

  1. 처음 자바스크립트 코드를 실행하는 순간 전역 컨텍스트(Global Context)가 콜 스택에 담긴다. 별도의 실행 명령 없이 브라우저에서 자동으로 실행되므로 자바스크립트가 실행되는 순간 전역 컨텍스트(Global Context)는 활성화된다. 전역 컨텍스트(Global Context)는 애플리케이션이 종료될 때까지 유지된다.
  2. example1() 함수가 호출되면, 자바스크립트는 example1() 함수에 대한 환경 정보를 수집해서 새로운 실행 컨텍스트(Execution Context)를 생성한 후 그 위에 전역 컨텍스트(Global Context)를 쌓는다.
  3. example1() 함수가 실행되다가 내부 함수 example2() 함수를 만나면 자바스크립트는 example2() 함수의 실행 컨텍스트(Execution Context)를 생성한다.
  4. 최상단에 쌓인 example2() 함수가 실행을 종료하면 example2() 함수에 의해 만들어진 실행 컨텍스트(Execution Context)는 콜 스택에서 제거된다.
  5. 마찬가지로 example1() 함수가 실행을 종료하면 example1() 함수에 의해 만들어진 실행 컨텍스트(Execution Context)는 콜 스택에서 제거된다.

위 코드를 통해 실행 컨텍스트(Execution Context)의 동작을 알아보았다.

한 실행 컨텍스트(Execution Context)가 콜 스택의 최상단에 쌓이는 순간이 곧 현재 실행할 코드에 관여하게 되는 시점임을 알 수 있었다.


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

실행 컨텍스트(Execution Context) 내부엔 3가지가 있다.

 

1️⃣ VariableEnvironment

- 구성: EnvironmentRecord, outerEnvironmentReference

- 현재 컨텍스트 내부의 식별자 정보인 EnvironmentRecord와 외부 환경 정보인 outerEnvironmentReference가 포함

- VariableEnvironment에 먼저 정보를 담고, 그대로 LexicalEnvironment에 복사하여 사용

 

2️⃣ LexicalEnvironment

- 구성: EnvironmentRecord, outerEnvironmentReference

- 초기에는 VariableEnvironment와 같지만 변경 사항이 실시간으로 적용

- VariableEnvironment는 초기 상태를, LexicalEnvironment는 최신 상태를 저장

 

3️⃣ ThisBinding

- this 식별자가 바라보고 있는 대상 객체

🔽 this와 관련한 내용은 아래 게시물을 참고하면 좋을 것 같다.

 

[JavaScript] this란?

자바스크립트에는 this라는 키워드가 있다. this는 문맥에 따라서 다양한 값을 가지는데 this가 쓰이는 함수를 어떤 방식으로 실행하느냐에 따라서 그 역할이 구별된다. this의 값들은 크게 4가지로

dori-coding.tistory.com

 

💡 outerEnvironmentReference

- 현재 호출된 함수가 선언될 당시의 LexicalEnvironment 참조 (두번째 예제 참고)

 

💡 EnvironmentRecord

- 현재 컨텍스트와 관련된 식별자와 식별자에 바인딩된 값이 기록되는 공간

- 실행 컨텍스트 내부 전체를 처음부터 끝까지 확인하며 순서대로 수집


아래 첫번째 예제 코드를 보자.

console.log(name); // undefined
var name = 'dori';

선언되기 전에 값을 호출했는데 에러가 나지 않고 undefined 즉, 할당되지 않았다고 출력됐다.

이것은 자바스크립트의 호이스팅과도 관련이 있다.

 

📌 호이스팅

자바스크립트 엔진이 실행 컨텍스트를 구성할 때 EnvironmentRecord에 식별자의 정보를 수집하기 때문에,

엔진은 함수를 실행하기도 전에 해당 컨텍스트 내부의 변수명들을 이미 알고 있는것이다.

 

정리하자면, LexicalEnvironment의 EnvironmentRecord의 경우 해당 컨텍스트 환경에 필요한 식별자와 식별자의 값이 기록된다.

그리고 함수 실행 시 실행 컨텍스트(Execution Context)가 생성되고 즉, 함수 실행보다 EnvironmentRecord 수집이 먼저 되므로 변수와 같은 식별자를 선언하기 전에 이미 알고 있는 것이다.


또 다른 두번째 예제 코드를 보자.

const place = 'cafe';

const ex = () => {
  const mintCake = {
    type: 'piece',
    name: 'mint choco'
  };
  const chocoCake = {
    type: 'piece',
    name: 'choco'
  };
  
  console.log(place);
  console.log(mintCake);
  console.log(chocoCake);
};

ex(); // 'cafe', { type: 'piece', name: 'mint choco' }, { type: 'piece', name: 'choco' }

console.log(mintCake); // Reference Error
console.log(chocoCake); // Reference Error

우선 위 코드는 ex() 함수 내부에선 전역 변수 place에 접근이 가능하며, 함수 내부에서 선언한 mintCake과 chocoCake 또한 접근이 가능하다.

하지만 외부에선 내부에 선언된 mintCake과 chocoCake에 접근할 수 없어 Referencce 에러가 났다.이건 outerEnvironmentReference 때문인데 ex() 함수가 선언될 당시의 outerEnvironmentReference는 글로벌 실행 컨텍스트의 LexicalEnvironment를 참조하고 있으며, 해당 환경의 EnvironmentRecord에는 변수 place와 같은 전역 변수들이 기록되어 있기 때문이다.그렇기 때문에 함수 내부에선 outerEnvironmentReference를 통해 상위 컨텍스트의 LexicalEnvirionment에 접근하여 EnvironmentRecord에서 변수 place를 사용할 수 있다.더불어 outerEnvironmentReference는 오직 자신이 선언될 당시의 LexicalEnvironment를 참조하기 때문에 순차적으로 접근이 가능하며, 여러 스코프에서 동일한 식별자가 생성되었다 하더라도 가장 먼저 발견된 식별자만 접근이 가능하다.

 

 

Comments