실행 컨텍스트

실행 컨텍스트에 대해 알아보기 이전에 자바스크립트의 소스 코드의 타입을 먼저 알아보자

소스 코드의 타입

  • 전역 코드: 전역에 정의된 함수, 클래스는 포함하지 않는 전역에 존재하는 코드

    • 전역스코프를 생성하고 var 키워드, 함수 선언문으로 선언된 전역함수를 전역객체에 바인딩하기위해 전역 컨텍스트를 생성함
  • 함수 코드: 함수내부에 존재하는 코드, 중첩 함수, 클래스 등은 포함되지 않음

    • 지역 스코프를 생성하고 지역변수, arguments, 매개변수 를 관리 하여 스코프 체인으로 연결 하기위해 함수 컨텍스트를 생성함
  • eval:eval의 인수로 전달되어 실행되는 코드

    • strict mode 에서만 독자적인 스코프를 생성함. 이를 위해 eval 컨텍스트를 생성
  • 모듈 코드:모듈 내부의 코드, 마찬가지로 함수, 클래스는 포함되지 않음

    • 모듈 별로 독립적인 스코프를 생성하기 위해 모듈 컨텍스트를 생성함

위 코드 타입들은 스코프를 생성하여 변수, 함수등을 관리하기 위해 컨텍스트를 생성한다

소스코드 평가와 실행

  • 소스코드 평가 실행 컨텍스트를 생성하고, 변수, 함수등의 선언문만 먼저 실행하여 선언된 식별자를 키로 실행컨텍스트가 관리하는 스코프(렉시컬 환경의 환경레코드)에 등록한다
  • 소스코드 실행 런타임이 시작되면 실행에 필요한 정보(변수, 함수의 참조)를 실행컨텍스트로 부터 제공받고 실행이 완료되면 그 결과를 실행컨텍스트에 전달하여 실행컨텍스트가 관리하는 스코프 내에 등록된다.
var x; // 소스코드 평가를 통해 렉시컬 환경의 환경 레코드내부에 x 변수가 등록됨
x = 1; // 값을 할당하기위해 실행컨텍스트내의 스코프체인을 검색하여 x 변수를 찾아 값을 할당하고 실행 컨텍스트에 등록하여 관리함

실행컨텍스트의 역할

const x = 1;
const y = 2;

function test(a) {
  const x = 10;
  const y = 20;
  console.log(a + x + y);
}

test(5);

console.log(x + y);

위 코드는 아래와 같은 순서로 평가 후 실행된다

  1. 전역 코드 평가 전역변수와 함수 선언문으로 선언된 전역 함수가 평가되어 전역스코프에 관리되고 전역객체의 프로퍼티와 메서드가 된다.
  2. 전역 코드 실행 런타임이 시작되고 선언된 전역변수를 스코프내에서 검색하여 값을 할당하고 함수가 호출된다. 함수가 호출될경우 코드 실행의 제어권이 함수에게 넘어가게되어 전역 코드실행이 잠시 중단된다.
  3. 함수 코드 평가 매개변수, 지역변수, arguments 가 평가되어 지역 스코프에 관리되고 this 바인딩이 결정된다.
  4. 함수 코드 실행 매개변수와 지역 변수에 값이 할당되고 console.log 문이 실행되는데 console을 스코프 체인을 통해 검색하고 log 프로퍼티를 프로토타입 체인을 통해 검색한 후 a + x + y 표현식 이 평가된다 a, x, y 또한 스코프 체인을 통해 검색한다. 함수 실행이 종료되면 다시 전역 코드로 돌아가 전역 코드실행을 이어서 진행한다.

이러한 코드 실행을 통해 스코프, 식별자, 코드 실행 순서등이 관리되어야하는데 이 역할을 실행컨텍스트가 하는 것이다.

식별자와 스코프는 렉시컬환경을 통해 관리하고 코드 실행순서는 실행 컨텍스트 스택에서 관리한다.

실행 컨텍스트 스택

const x = 1;
const y = 2;

function test(a) {
  const x = 10;
  const y = 20;
  console.log(a + x + y);
}

test(5);

console.log(x + y);

위 코드를 다시 가져왔다. 위 코드는 전역 컨텍스트와 함수 컨텍스트가 존재하며 스택 자료 구조를 가진 실행컨텍스트 스택에서 관리된다.

먼저 들어온 컨텍스트가 가장 나중에 빠져 나가는 LIFO(Last In First Out) 방식으로 진행된다.

  1. 전역 코드 평가와 실행 전역 컨텍스트가 실행컨텍스트 스택에 push된 후 전역 변수 x, y 전역 함수 test 가 평가되어 선언된다. 이후 코드가 실행되어 전역변수에 값이 할당되고 전역 함수가 실행된다.
  2. test 함수 평가와 실행 함수 컨텍스트가 실행컨텍스트 스택에 push되고 지역변수, 매개변수, arguments가 평가된다. console.log 가 호출된다(이 과정은 위에서 언급된 함수 코드실행과정과 같다)
  3. 전역 코드로 복귀 test 함수가 실행컨텍스트 스택에서 pop 되고 전역 컨텍스트가 실행제어권을 돌려 받는다. console.log 가 호출되고 전역 컨텍스트가 pop 되면서 실행이 종료된다

중첩함수가 존재하는 경우 2번 과정이 한번 더 반복된다고 인지하면 된다.

스택 자료구조를 갖는 실행 컨텍스트 스택의 특징 덕분에 실행컨텍스트 스택의 최상위 컨텍스트가 항상 현재 실행중인 컨텍스트가 된다.

이 최상위 컨텍스트를 실행중인 실행 컨텍스트 라고 부른다

렉시컬 환경

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

렉시컬 환경은 key value를 갖는 객체형태의 스코프를 생성하여 식별자를 키로 등록하고 식별자에 바인딩된 값을 관리한다.

즉 렉시컬 환경은 스코프를 구분하여 식별자를 등록하고 관리하는 저장소 역할을 하는 렉시컬 스코프의 실체!

렉시컬환경은 아래의 요소로 구성된다.

  1. 환경레코드 스코프를 포함한 식별자를 등록하고 식별자에 바인딩된 값을 관리하는 저장소
  2. 외부 렉시컬 환경에 대한 참조 상위스코프를 가리키며 해당 실행컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 뜻한다.

실행 컨텍스트의 생성과 식별자 검색과정

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. 전역 환경 레코드 생성

      1. 객체 환경 레코드 생성
      2. 선언적 환경 레코드 생성
  3. this 바인딩
  4. 외부 렉시컬 환경 참조 결정

전역 환경 레코드 생성

객체 환경 레코드와 선언적 환경 레코드로 구성된다. 객체 환경 레코드에는 앞서 생성된 전역 객체의 var 로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수, 표준 빌트인객체, 빌트인 전역프로퍼티, 빌트인 전역함수를 관리하며 선언적 환경 레코드는 let, const 키워드로 선언한 전역 변수를 관리한다.

객체 환경 레코드 생성

전역 환경 레코드를 구성하는 컴포넌트 객체로서 BindingObject(전역 객체 생성에서 생성된 전역 객체)라는 객체와 연결된다. 전역 코드 평가 과정에서 var 키워드로 선언한 전역 변수, 함수 선언문으로 정의된 전역 함수는 전역 환경 레코드의 객체 환경 레코드에 연결된 BindingObject를 통해 전역 객체의 프로퍼티와 메서드가 된다.

이미 생성되어있는 전역 객체에 var키워드로 선언한 변수가 등록되어 undefined가 바인딩 되었기 때문에 코드 실행 단계 이전에도 참조할 수 있는 것이다. (변수 호이스팅이 발생하는 원인)

함수 선언문은 함수 이름과 동일한 식별자를 키로 등록하고 생성된 함수 객체를 즉시 할당하기 때문에 변수호이스팅과 함수호이스팅에 차이가 발생하는 이유이다.

선언적 환경 레코드 생성

var 키워드로 선언한 전역 변수와 함수 선언문으로 선언된 전역 함수외의 변수와 함수는 선언적 환경 레코드에서 관리된다.

let, const 는 var 전역변수와 다르게 선언적 환경레코드에 식별자를 키로 등록은 되지만 초기화가 되지 않아 일시적 사각지대(TDZ)에 빠지게 되는것이다.

this 바인딩

전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩되며, 일반적으로 내부 슬롯에는 전역 객체가 바인딩된다. 전역 코드에서 this를 참조하면 전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 바인딩 된 객체가 반환된다.

또한 this 바인딩은 전역 환경레코드와 함수 환경 레코드에만 존재한다.

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

현재 평가 중인 소스코드를 포함하는 외부 소스코드의 렉시컬 환경인 상위 스코프를 가리킴 이를 통해 단방향 링크드 리스트인 스코프체인을 구현

전역 코드 실행

전역 코드가 순차적으로 실행되며 이 때 실행 중인 실행 컨텍스트에서 식별자를 검색하고 어느 스코프의 식별자를 참조하면 되는지 결정하는 식별자 결정이 발생

함수 코드 평가

  1. 함수 실행 컨텍스트 생성
  2. 함수 렉시컬 환경 생성

    1. 함수 환경 레코드 생성
    2. this 바인딩
    3. 외부 렉시컬 환경에 대한 참조 결정

함수 환경 레코드 생성

매개변수, arguments, 함수 내부에서 선언한 지역 변수와 중첩함수를 등록하고 관리

this 바인딩

함수 환경 레코드의 [[ThisValue]] 내부 슬롯에 this가 바인딩된다. 함수 호출방식에 따라 달라지며 위의 코드의 경우에서 foo 함수의 this에는 전역객체가 바인딩된다. 함수에서 this를 참조하면 [[ThisValue]] 내부슬롯에 바인딩된 객체가 반환됨

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

함수 정의가 평가된 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경 참조가 할당 자바스크립트 엔진은 현재 실행 중인 실행 컨텍스트의 렉시컬 환경인 상위 스코프를 함수 객체의 내부 슬롯 [[Environment]]에 저장

함수 코드 실행종료

실행컨텍스트 스택에서 pop되어 제거되고 전역 실행 컨텍스트가 실행중인 컨텍스트가 됨

전역 코드 실행 종료

실행 컨텍스트 스택에서 pop되고 실행컨텍스트 스택은 비어있게 됨

실행 컨텍스트와 블록 레벨 스코프

모든 코드블록은 블록 레벨 스코프를 따르기 때문에 해당 코드블록이 실행되면 선언적 환경레코드를 갖는 렉시컬환경을 새로 생성하여 기존의 렉시컬 환경을 교체한다. 이때 생성된 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 코드 블록이 실행되기 전의 렉시컬환경을 가리킨다.