자바스크립트 실행 컨텍스트 복습 및 정리하기

    반응형

    자바스크립트 실행 컨텍스트

     

    자바스크립트 문법을 공부하면서 가장 이해가 어려운 부분을 손에 꼽으라고 한다면 실행 컨텍스트 부분이 아닐까 싶다. 낯선 단어들과 이해가 어려운 내용들로 되어있지만 자바스크립트 엔진이 코드를 실행하는 방식을 이해하는데 중요한 역할을 하기 때문에 꼭 짚고 반복학습하면서 넘어가기로 결정했다. 이해하고 정리한 내용을 작성하려고 한다.

     

    실행 컨텍스트


    실행 컨텍스트란 무엇인가? "실행 컨텍스트는 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역" 혹은 "실행할 코드에 제공할 환경 정보들을 모아놓은 객체"라는 설명이 많다. 무슨 설명인지 하는 마음이 크지만 예시 코드와 그림들을 보면 좀 더 이해가 쉬워질 수 있다.

     

    보통 자바스크립트가 실행되는 런타임은 브라우저 혹은 Node.Js 환경에서 실행된다. 이러한 환경에서 실행되는데 자바스크립트 엔진은 소스코드를 평가하는 과정과 실행하는 과정 2 과정으로 나누어 진행한다. 그리고 소스코드를 평가하는 과정에서 실행 컨텍스트가 등장한다.

     

    let x = 10;
    
    function outer() {
      let y = 20;
      
      function inner() {
        let z = 30;
        console.log(x + y + z);
      }
      
      inner();
    }
    
    outer();

     

    그렇다면 이 예시코드를 평가하고 실행하는 과정으로 나누어 보려 한다. 우선 소스코드를 평가하기 위해서 이 소스코드에서 어떠한 변수가 선언되고, 어떠한 함수가 호출되는지 등의 정보들이 필요하다. 이때 이러한 정보들을 모아놓은 것을 실행컨텍스트 라고 볼 수 있다. 

     

    위 코드의 실행 순서를 보면 1. 전체 코드(전역)를 평가한다. x와 outer( )는 선언문이니 설명서라고 볼 수 있는 실행컨텍스트에 등록된다. 2. 전체 코드(전역)를 실행한다. 이제 순자척으로 코드가 실행되는데 outer( ) 라는 함수가 호출되었고 함수가 호출되면 코드 실행을 중단하고 outer( ) 함수 내부로 들어간다. 3. 함수 내부로 들어왔으니 다시 함수 코드를 평가한다. 이때에도 선언된 변수와 함수들을을 함수 실행 컨텍스트에 등록한다. 4. 함수 코드를 실행한다. 실행하다 보니 다시 inner( ) 함수가 호출되었다. 그렇다면 다시 함수 실행을 중단하고 5. inner( ) 함수 내부로 들어가 코드를 평가하고 실행한다. 이러한 순서로 코드가 평가되고 실행되는데 이처럼 코드가 실행되기 위해서 스코프, 식별자, 코드의 실행 순서 등을 관리가 필요하다는 걸 알 수 있다. 이 모든 것을 관리하는 것이 실행 컨텍스트 라고 볼 수 있다. 이러한 코드의 실행 순서를 실행 컨텍스트 스택에서 관리하는데 그림으로 이해하면 좀 더 쉽다.

     

    실행 컨텍스트 스택 순서

     

     

    1. 실행 컨텍스트 종류

    ECMAscript 사양은 소스코드를 4가지 타입으로 구분한다. 이 4가지 타입에 따라 4가지의 소스코드에 따라 실행 컨텍스트를 생성하는 과정, 관리하는 부분이 다르다.

    • 전역 코드 : 전역 컨텍스트 / 전역에 존재하는 소스코드로 전역에 정의된 함수, 클래스 등이며 내부 코드는 포함되지 않는다. 
    • 함수 코드 : 함수 컨텍스트 / 함수 내부에 존재하는 소스코드로 함수 내부의 중첩된 함수나 클래스 등의 내부 코드는 포함하지 않는다.
    • eval 코드 : eval 컨텍스트 / eval 함수에 인수로 전달되어 실행되는 소스 코드로 strict mode에서 자신만의 독자적인 스코프를 생성한다.
    • 모듈 코드 : 모듈 컨텍스트 / 모듈 내부에 존재하는 소스코드로 모듈 내부에 함수, 클래스 등의 내부 코드는 포함하지 않는다. 모듈별로 독립적인 모듈 스코프를 생성하고 평가한 뒤 모듈 실행 컨텍스트가 생성된다.

     

    2. 실행 컨텍스트 구성 요소

    예시 코드와 그림을 통해 실행 컨텍스트가 무엇인지 조금이나마 이해가 되었다면 더욱더 궁금해진다. 그렇다면 실행 컨텍스트는 어떻게 구성되어 있을까? 

    • Variable Environment (VE) 
      Variable Environment는 현재 실행 컨텍스트 내부에 있는 식별자 정보와 외부의 환경정보를 가지고 있다. 실행 컨텍스트가 실행되는 시점에 스냅샷을 가지고 유지하며 변경사항이 반영되지 않는다.

    • Lexical Environment (LE) 
      Lexical Environment는 초기에 Variable Environment와 완전히 같다. 하지만 코드 실행 중 변경사항이 실시간으로 반영된다.

    • This Binding
      현재 실행 컨텍스트에서 this 키워드가 참조하는 객체를 결정한다. (다른 글에서 추가로 정리)

     

    3. Lexical Environment (LE) 구성요소

    ES6 이후의 Lexical Environment의 역할이 확장되면서 많은 사용이 되었고 아무래도 많은 글들이 Lexical Environment에 대해 더 정의하고 있다. Lexical Environment는 Environment Record와 Outer Lexical Environment Reference를 가지고 있다.

     

    • 환경 레코드 (Environment Record)
      스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩 된 값을 관리하는 일종의 저장소이다. 환경 레코드는 Declarative (선언적 환경 레코드)와 Object Environment Records (개체 환경 레코드)로 나누어지는데 대부분의 변수, 함수와 같은 식별자들은 선언적 환경 레코드에 저장이 된다. 이곳에서 호이스팅이란 개념이 등장한다.

      실행 컨텍스트가 생성되면서 컨텍스트 내부를 시작부터 끝까지 순서대로 수집한다. 하지만 두 번째 코드는 name이라는 식별자가 나중에 선언되었음에도 undefined를 출력한다. 환경 레코드가 컨텍스트 내부의 식별자들의 정보를 이미 알고 있다는 의미이며 이것을 최상단으로 끌어올렸다는 추상화된 개념이 바로 호이스팅이다.

    var name = "minhoo"
    console.log(name) // "minhoo"
    
    -----------------------------------------
    
    console.log(name) // undefined
    var name = "minhoo"

     

    • 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)
      외부 렉시컬 환경 참조는 상위 스코프를 가리킨다. 여기서 말하는 상위 스코프란 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 이야기한다. 외부 렉시컬 환경 참조를 통해 스코프 체인이 가능하다. 쉽게 말해 외부 렉시컬 환경 참조를 통해 상위 실행 컨테스트에 데이터를 참조할 수 있다고 생각하면 이해가 편하다.

    const name = 'minhoo';
     
    const user = () => {
      const user1 = {
        age: 10,
        name: 'john'
      };
      
      const user2 = {
        age: 24,
        name: 'brlick'
      };
    
      console.log(name); //   minhoo
      console.log(user1); // {age: 10, name: 'john'}
      console.log(user2); // {age: 24, name: 'brlick'}
    }
    
    user();
    console.log(user1); // ReferenceError: user1 is not defined
    console.log(user2); // ReferenceError: user2 is not defined

     

    위 코드를 실행하면 주석과 같은 값이 출력된다. 전역 컨텍스트에 console.log(user1), console.log(user2)는 전역 컨텍스트에 Lexical Environment의 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)는 식별자 정보를 찾는데 전역 컨텍스트의 외부 렉시컬 환경 참조는 비어있기 때문에 값이 정의되지 않았다는 에러가 발생한다.

    그러나 user( ) 함수의 실행 컨테스트에 Lexical Environment의 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)는 상위 컨텍스트인 전역 컨테스트를 참조할 수 있기 때문에 user( ) 함수 내부에서 console.log(name)을 출력해도 상위 컨텍스트에서 데이터를 찾아 참조할 수 있다.

     

    정리한다면..

    1. 실행 컨텍스트는 자바스크립트의 실행 환경을 추상화한 개념이자 엔진 내부에서 코드 실행에 필요한 모든 정보를 포함하는 객체이다.

    2. 실행 컨텍스트는 VE(Variable Environment), LE(Lexical Environment)로 구성되어 있고 VE는 초기의 형태를 유지하고 변화가 없지만 LE는 코드 실행 중 변경사항이 반영된다. 

    3. LE의 환경 레코드(Environment Record)에서는 여러 식별자들의 정보를 저장하며 주로 사용되는 식별자들은 선언적 환경 레코드 Declarative Environment Records (선언적 환경 레코드)에 저장된다.

    4. LE의 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)를 통해 상위 실행 컨텍스트의 데이터를 참조할 수 있다. 

    5. 환경 레코드(Environment Record)가 식별자를 수집하는 과정에서 호이스팅이라는 개념이 생성되었으며, 추상화된 개념이다.
    반응형

    댓글