TIL
실행 컨텍스트(Execution Context) 2
Execution_Context
JS
2023.09.26.
목차보기
    • 순서를 간략하게 정리하자면 아래와 같다.
    1. 전역 객체 생성 -> 2. 전역 코드 평가 -> 3. 전역 코드 실행
    -> 4. 함수 코드 평가 -> 5. 함수 코드 실행
    -> 새로 실행되는 함수가 있다면 앞 단계를 반복 -> 종료 시점에 스택에서 pop -> ...

    1. 전역 객체 생성

    • 전역 코드가 평가되기 이전에 생성된다.
      • 전역 객체란?
        • 빌트인 전역 프로퍼티, 빌트인 전역 함수, 표준 빌트인 객체가 추가되면 동작환경에 따라 클라이언트 사이드 Web API(DOM, fetch 등) 또는 특정 환경을 위한 호스트 객체를 포함한다.
    • Object.prototype을 상속받는다.
      • 즉, 전역 객체도 프로토타입 체인의 일원이다.

    2. 전역 코드 평가

    • 순서
      1. Global Execution Context(전역 실행 컨텍스트) 생성
        → 실행 컨텍스트 스택에 push
      2. Global Lexical Environment(전역 렉시컬 환경) 생성
        → 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩한다.
        1. 2.1. GlobalEnvironmentRecord(전역 환경 레코드)생성
          1. 2.1.1. Object Environment Record(객체 환경 레코드) 생성
          2. 2.1.2. Declarative Environment Record(선언전 환경 레코드) 생성
        2. 2-2. this 바인딩
          → [[GlobalThisValue]] 내부 슬롯에 this가 바인딩되는데 일반적으로 전역 코드에서 this는 전역 객체를 가리킨다.
          → this 바인딩은 전역 환경 레코드를 구성하는 객체/선언적 환경 레코드에는 없고, 전역 환경 레코드와 함수 환경 레코드에만 존재한다.
        3. 2.3. Outer Lexical Enviromnment Reference(외부 렉시컬 환경에 대한 참조) 결정
          → 전역에서는 상위 스코프는 없다. → 즉, 스코프 체인에 종점이므로 null을 할당한다.

    전역 환경 레코드에서 왜 2가지로 나눠진걸까?

    • 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 제공한다.
    • 모든 전역 변수가 전역 객체의 프로퍼티가 되는 ES6 이전에는 전역 객체가 전역 환경 레코드의 역할을 수행했다. (기존의 전역 객체가 관리하던 var 키워드) ↔ ES6의 let, const키워드로 선언한 전역 변수는 전역 객체의 프로티가 되지 않고 개념적인 블록 내에 존재하게 된다.

    객체 환경 레코드는 var 키워드로 선언한 전역 변수(함수 선언문으로 정의한 전역 함수 등)
    선언전 환경 레코드는 let/const 키워드로 선언한 전역 변수
    로 구분하여 관리하기 위해 2가지로 나뉜다.

    BindingObject는 어디서 생성되고, 어디와 연결되는가?

    • 전역 객체 생성에서 생성된 전역 객체이다.
    • var 키워드로 선언한 전역 변수, 함수 선언문의 정의된 전역 함수는 전역 환경 레코드의 객체 환경 레코드에 연결된 BindingObject를 통해 전역 객체의 프로퍼티와 메서드가 된다.

    ⇒ var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수가 전역 객체의 프로퍼티와 메서드가 되고 전역 객체를 가리키는 식별자(window)없이 전역 객체의 프로퍼티를 참조할 수 있는 메커니즘이다. (ex. window.alert를 alert로 참조하는)

    변수 호이스팅, 함수 호이스팅

    • var 키워드로 선언한 변수는 선언 단계와 초기화 단계가 동시에 진행된다!
      • 객체 환경 레코드에 바인딩된 bindingObject를 통해 전역 객체에 변수 식별자를 키로 등록한 다음 undeined로 바인딩한다 → 참조 가능(선언문 전에) ⇒ 변수 호이스팅
    • 함수 선언문으로 정의한 함수도 마찬가지로 bindingObject를 통해 키가 등록되고, 생성된 함수 객체를 즉시 할당한다 → 참조,호출 가능(선언문 전에) ⇒ 함수 호이스팅 (변수 호이스팅과 차이)

    const와 let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 될 수 없다.

    • 전역 객체 프로퍼티가 되지 않기 때문에 window.y와 같이 전역 객체의 프로퍼티로서 참조할 수 없다.
    • const 키워드는 선언과 초기화 단계가 분리되어있어, 런타임에 변수 선언문에 도달하기 전까지 TDZ에 빠지며 uninitialized로 바인딩된다.
      (uninitialized : 초기화 단계가 진행되지 않아 변수에 접근할 수 없음을 나타낼 때 사용한다.)

    3. 전역 코드 실행

    • 코드가 순차적으로 실행되면서 변수 할당문이 실행되어 값을 할당한다.

    • 이 때, 선언된 식별자인지 확인해야한다.

    • 식별자가 동일한 이름으로 다른 스코프에 여러개 존재할 수 도 있어, 어느 스코프의 식별자를 참조하면 되는지 결정하는 것을 식별자 결정 이라 한다.

      • 식별자 결정을 위해 식별자를 검색할 때는 실행 중인 실행 컨텍스트에서 식별자를 검색한다
        • 실행 컨텍스트의 렉시컬 환경의 환경 레코드에 등록되어있다 → 선언되었다면!
      • 검색 시 없다면 상위 스코프로 이동해서 식별자를 검색한다. ⇒ 스코프 체인의 동작 원리다.
      • 만약, 전역 환경에서도 없다면 참조 에러를 발생시킨다.

    4. 함수 코드 평가

    • 함수 호출 시 전역 코드의 실행을 일시 중단하고 해당 함수 내부도 코드 제어권이 이동한다.
    • 순서
      1. fun1 Execution Context(fun1 실행 컨텍스트) 생성
        → 실행 컨텍스트 스택에 `push`
      2. fun1 Lexical Environment(fun1 렉시컬 환경) 생성
        → 렉시컬 환경을 생성하고 전역 실행 컨텍스트에 바인딩한다.
        1. 2.1. FunctionEnvironmentRecord(함수 환경 레코드)생성
          → 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩함수를 등록하고 관리한다.
        2. 2-2. this 바인딩
          → 일반 함수로 호출되었다면, this는 전역 객체를 가리킨다
        3. 2.3. Outer Lexical Enviromnment Reference(외부 렉시컬 환경에 대한 참조) 결정

    외부 렉시컬 환경에 대한 참조 결정 시 상위 스코프는 어떻게 아는가?

    • 자바스크립트는 함수를 어디서 호출했는지가 아니라 어디에 정의했는지에 따라 상위 스코프를 결정한다.
    • 함수 객체는 자신이 정의된 스코프(상위 스코프)를 기억한다.
    • 자바스크립트 엔진은 함수 정의를 평가하여 함수 객체를 생성할 때 현재 실행 중인 실행 컨텍스트의렉시컬 환경, 즉 함수의 상위 스코프를 함수 객체의 내부 슬록 [[Environment]]에 저장한다.
    • 함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 할당되는 것은 바로 함수의 상위 스코프를 가리키는 함수 객체의 내부 슬롯 [[Environment]]에 저장된 렉시컬 환경의 참조다.
    • 즉, 함수 객체의 내부 슬롯 [[Environment]]가 바로 렉시컬 스코프를 구현하는 메커니즘이다.

    5. 함수 코드 실행

    • 변수 값 할당 등 진행

    마무리

    또 다른 함수를 실행하고 한다면, 함수 코드 평가와 실행을 반복할 것이다.

    더 이상 실행할 코드가 없다면, 해당 함수는 실행이 종료되고 컨텍스트 스택에서 pop 제거된다.

    렉시컬 환경은 실행 컨텍스트에 의해 참조되기는 하지만 독립적인 객체다. 객체를 포함한 모든 값은 누군가에 의해 참조되지 않을 때 비로소 가바리 컬렉터에 의해 메모리 공간의 확보가 해제되어 소멸한다.

    위처럼 pop을 진행하다보면, 실행 컨텍스트 스택에는 아무것도 남이있지 않게 된다.

    console.log는 어떻게 처리가 되는가?

    console.log(a)
    • console 식별자를 스코프 체인에서 검색한다.
    • console 식별자는 객체 환경 레코드의 bindingObject를 통해 전역 객체에서 찾을 수 있다.
    • console 객체에서 log 메서드를 검색한다.
      • console 객체의 프로토타입 체인을 통해 메서드를 검색한다.
      • log 메서드는 상속된 프로퍼티가 아니라 console 객체가 직접 소유하는 프로퍼티다.
        console.hasOwnProperty('log') // true
    • 괄호 안에 있는 변수도 스코프 체인에서 검색해서 반환한다.

    이전글 : 실행 컨텍스트 개념과 역할, 스택 그리고 렉시컬 환경

    Front Developer 김정희 😊

    글 목록보기