실행 컨텍스트

이재윤·2021년 9월 5일
0

JavaScript

목록 보기
5/10
post-thumbnail

💻 소스코드의 평가와 실행

자바스크립트 엔진은 소스코드를 '평가'와 '실행' 두가지 과정으로 나누어 처리합니다.

  1. 평가
    실행 컨텍스트를 생성한 후, 변수, 함수등의 선언문만 실행해 생성된 식별자를 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록합니다.
  2. 실행
    선언문을 제외한 소스코드가 순차적으로 실행되며, 소스코드 실행에 필요한 정보(변수, 함수의 참조)를 컨텍스트가 관리하는 스코프에서 검색해 얻습니다. 소스코드의 실행결과는 다시 컨텍스트가 관리하는 스코프에 등록이 됩니다.

💻 실행 컨텍스트

식별자(변수, 함수, 클래스 등의 이름)를 등록하고 관리하는 스코프(by 렉시컬 환경)와 실행 순서 관리(by 실행 컨텍스트 스택)를 구현한 내부 메커니즘이다.

💻 실행 컨텍스트 스택

const x = 1;
function foo() {
  const y = 2;
  function bar() {
    const z = 3;
    console.log(x + y + z);
  }
  bar();
}
foo();

  1. 전역 코드의 평가와 실행
    1. 전역 코드를 평가후 전역 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 푸쉬 합니다. 이때, 전역변수 xfoo는 전역 실행 컨텍스트에 등록이 됩니다.
    2. 실행단계에서 변수 x에 값이 할당이 되며 전역함수 foo가 호출이 됩니다.

  2. foo 함수 코드의 평가와 실행
    1. foo함수가 호출되면 전역 코드의 실행이 멈추게 됩니다.
    2. 자바스크립트 엔진은 foo함수 내부의 함수 코드를 평가하여 foo함수 실행 컨텍스트를 생성한 후 컨텍스트 스택에 푸쉬합니다. 이때, foo함수의 지역변수 y와 중첩함수 bar가 실행 컨텍스트에 등록됩니다.
    3. 실행단계에서 y에 값이 할당되며, bar가 호출이 됩니다.

  3. bar 함수 코드의 평가와 실행
    1. bar함수가 호출이 되면 foo함수 실행이 멈추게 됩니다.
    2. bar함수 내부의 함수 코드를 평가해 bar함수 실행 컨텍스트를 생성한 후 컨텍스트 스택에 푸쉬합니다. 이 때 지역변수 z가 컨텍스트에 등록됩니다.
    3. 실행단계에서 z에 값이 할당되며 console.log메서드를 호출한 이후 bar 함수는 종료됩니다.

      console.log도 함수 이기 때문에 실행 컨텍스트를 만드나 그림에서는 생략되었습니다.

  4. foo 함수 코드로 복귀
    1. bar함수가 종료되면 bar함수 실행 컨텍스트를 컨텍스트 스택에서 제거(pop)합니다.
    2. foo함수도 더이상 실행 할 코드가 없기 때문에 종료됩니다.

  5. 전역 코드로 복귀
    1. foo함수를 실행 컨텍스트 스택에서 제거(pop)합니다.
    2. 전역 코드에서 더이상 실행할 코드가 없으므로 전역 컨텍스트로 컨텍스트 스택에서 제거됩니다.

💻 렉시컬 환경

식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트 입니다.

❗ 컨텍스트는 Lexical Environment와 Variable Environment로 구성되어 있으며, 생성초기에 동일한 Lexical Environment를 참조하게 됩니다. 이후 상황에 따라 달라질 수 있지만 이글에서는 하나의 Lexical Environment로 통일해 설명합니다.

Lexical Environment은 두 개의 구성요소로 이루어져 있습니다.

  1. Environment Record
    스코프에 포함된 식별자를 등록하고, 바인딩된 값을 관리하는 저장소 입니다. 소스코드의 타입에 타라 관리하는 내용에 차이가 있습니다.
  2. Outer Lexical Enviroment Reference
    상위 스코프를 가리킵니다.

💻 코드 실행의 전체 과정

실행 컨텍스트 스택에서 설명한 코드 실행과정을 Lexical Environment를 포함해서 더욱 자세하게 설명해 보도록 하겠습니다.

var x = 1;
const y = 2;

function foo(a) {
  var x = 3;
  const y = 4;
  
  function bar(b) {
    const z = 5;
    console.log(a + b + x + y + z);
  }
  bar(10);
}
foo(20);
  1. 전역 객체 생성
    전역객체가 전역 코드 평가 이전에 생성이 됩니다.

  2. 전역 코드 평가

    1. 전역 실행 컨텍스트 생성
      전역 실행 컨텍스트를 생성 후 컨텍스트 스택에 푸쉬합니다.

    2. Lexical Environment 생성

      전역 Lexical Enviroment를 생성 후 실행 컨텍스트에 바인딩 합니다.
      2.1 객체 환경 레코드 생성전역 환경 레코드를 구성하고 있는 객체 환경 레코드는 binding object를 참조하게 됩니다. binding object는 처음에 생성했던 전역 객체 입니다.

      ❗ 전역 코드 평가 과정에서 var 키워드로 선언한 전역 변수와 함수 선언문으로 정의된 함수는 전역객체의 프로퍼티와 메서드가 됩니다. 또한 전역객체가 전역 환경 레코드에 바인딩되기 때문에 window 키워드 없이 전역객체에 접근할 수 있습니다.
      ❗ var로 선언된 변수나 함수는 선언과 동시에 초기화가 일어납니다. 다시말해 binding object를 통해 전역 객체에 변수 식별자를 키로 등록한다음 undefined를 할당합니다. 따라서 실행전에 변수를 식별할 수 있습니다. 이는 변수 호이스팅이 발생하는 원인이 됩니다.
      ❗ 함수 선언문으로 정의한 함수는 식별자 등록과 동시에 객체를 할당하기 때문에 실행 이전에 호출할 수 있습니다.

      2.2 선언적 환경 레코드 생성

      let, const 키워드로 선언한 전역변수는 선언적 환경 레코드에 등록되고 관리됩니다.

    3. this 바인딩

      전역 환경 레코드의 내부 슬롯에 this가 바인딩 됩니다. 일반적으로 전역 코드에서 this는 전역 객체를 가리키므로 전역 환경 레코드의 내부 슬롯에는 전역 객체가 바인딩 됩니다.

    4. 외부 렉시컬 환경에 대한 참조 결정

      상위 스코프를 참조하는 단계입니다. 전역 코드의 경우 상위 스코프는 존재하지 않기때문에 null로 초기화 됩니다.

  3. 전역 코드 실행

    선언문을 제외한 전역 코드가 실행이 됩니다. 변수 할당문이나 함수 호출문을 실행하기 전, 실행 컨텍스트의 Lexical Environment의 Environment Record를 통해 선언된 식별자인지 확인해야 합니다. 그 후 값을 할당하게 됩니다.

    스코프 체인이란?
    현재 실행 중인 컨텍스트에서 식별자를 검색하게 되는데 만약 존재 하지 않을경우 Lexical Environment의 Outer Lexical Environment Reference를 통해 상위 스코프에서 식별자를 검색하게 됩니다.

  4. foo 함수 코드 평가

    1. 함수 실행 컨텍스트 생성

      foo 함수 실행 컨텍스트를 생성합니다. 함수 실행 컨텍스트는 Lexical Environment가 완성된 다음 컨텍스트 스택에 푸쉬 됩니다.

    2. 함수 Lexical Environment 생성
      2.1 함수 Environment Record 생성

      함수 Lexical Environment를 구성하는 컴포넌트 중 하나인 함수 Environment Record는 매개변수, arguments 객체, 함수 내부에서 선언한 지역변수와 중첩 함수를 등록합니다.

      2.2 this 바인딩

      foo함수가 일반함수로 호출되었으므로 this는 전역객체를 가리키게 됩니다.
      2.3 외부 렉시컬 환경에 대한 참조 결정
      외부 렉시컬 환경에 대한 참조에 foo 함수 정의가 평가된 시점에 실행 중이던 컨텍스트의 렉시컬 환경이 할당 됩니다. foo함수가 평가된 시점에 실행 중이던 컨텍스트는 전역 컨텍스트 이므로 전역 컨텍스트의 렉시컬 환경을 참조하게 됩니다.

      ❗ 자바스크립트는 어디서 실행했는지가 아닌 어디서 선언했는지에 따라 상위 스코프가 결정되는 방식입니다.
  5. foo 함수 코드 실행

    매개변수에 값이 할당되며, 변수 할당문이 실행되어 지역변수에 값이 할당 됩니다. 또한 bar함수가 실행 됩니다.

  6. bar 함수 코드 평가

    foo함수 평가 단계와 동일합니다.

  7. bar 함수 코드 실행

  8. bar 함수 코드 실행 종료
    bar실행 컨텍스트가 종료되고 컨텍스트 스택에서 제거됩니다.
    실행 컨텍스트가 제거 되었다고 해서 bar함수의 Lexical Environment까지 제거되는 것은 아닙니다. 만약 누군가가 참조하고 있다면, bar함수의 Lexical Environment는 사라지지 않습니다.

  9. foo 함수 코드 실행 종료

  10. 전역 코드 실행 종료

💻 블록 레벨 스코프

let, constvar와 달리 블록 레벨 스코프를 따릅니다. 블록 레벨 스코프를 실행 시킬경우 컨텍스트가 어떻게 변하는지 살펴보겠습니다.

let x = 1;
if (true) {
  let x = 10;
  console.log(x);
}
console.log(x);

코드의 블록이 실행되면 선언적 환경을 갖는 Lexical Environment를 생성 후 기존의 Lexical Environment를 대체하게 됩니다.
아래의 그림과 같습니다.

코드 블록의 실행이 종료되면 다시 이전의 Lexical Environment로 돌립니다.

0개의 댓글