ECMAScript 사양은 소스코드를 4가지 타입으로 구분합니다.
- 전역 코드 : 전역에 존재하는 소스코드
- 함수 코드 : 함수 내부에 존재하는 소스코드
- eval 코드 : 빌트인 전역 함수인 eval 함수에 인수로 전달되어 실행되는 소스코드
- 모듈 코드 : 모듈 내부에 존재하는 소스코드
소스코드를 4가지 타입으로 구분하는 이유는 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문입니다.
실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역입니다. 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 매커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리됩니다.
소스코드의 평가와 실행
자바스크립트 엔진은 소스코드 평가 단계와 소스코드 실행 단계로 나눠서 소스코드를 실행합니다. 평가 단계에서는 실행 컨텍스트를 생성하고 변수와 함수 같은 선언문을 먼저 실행해서, 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 렉시컬 환경의 환경 레코드에 등록합니다. 소스코드의 평가 단계가 끝나면 선언문을 제외한 소스코드가 위에서부터 순차적으로 실행되기 시작합니다. 즉, 실행 단계가 런타임에 해당합니다. 소스코드 실행에 필요한 정보는 실행 컨텍스트가 관리하는 스코프에서 검색해서 얻습니다. 그리고 변수 값의 변경과 같은 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록됩니다.
실행 컨텍스트 스택
실행 컨텍스트 스택은 코드 실행 숭서를 관리합니다. 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트입니다. 이를 실행 중인 실행 컨텍스트라 부릅니다.
렉시컬 환경
렉시컬 환경은 식별자와 식별자에 바인딩된 값, 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트입니다. 실행 컨텍스트 스택이 코드의 순서를 관리한다면, 렉시컬 환경은 스코프와 식별자를 관리합니다.
실행 컨텍스트가 초기에 생성되면 LexicalEnvironment 컴포넌트와 VariableEnvironment 컴포넌트로 구성됩니다. 초기에는 이 둘의 컴포넌트는 하나의 동일한 렉시컬 환경을 참조합니다.
또 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조 컴포넌트로 구성됩니다. 환경 레코드 컴포넌트는 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소입니다. 외부 렉시컬 환경에 대한 참조 컴포넌트는 상위 스코프를 가리킵니다. 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 상위 코드의 렉시컬 환경을 말합니다. 외부 렉시컬 환경에 대한 참조 컴포넌트를 통해서 스코프 체인이 형성되는 겁니다.
실행 컨텍스트가 실행되는 순서
var a = 1;
const b = 2;
function foo(c) {
var a = 3;
const y = 4;
return a + y + c;
}
foo(3);
- 전역 코드가 평가되기 이전에 전역 객체를 생성합니다. (ex.빌트인 전역 함수, 빌트인 전역 프로퍼티 등)
- 소스코드가 로드되면 자바스크립트 엔진은 전역 코드를 평가합니다.
- 전역 실행 컨텍스트 생성하고 실행 컨텍스트 스택에 푸시합니다.
- 전역 렉시컬 환경을 생성하고 전역 실행 컨텍스트의 LexicalEnvironment 컴포넌트와 VariableEnvironment 컴포넌트에 바인딩합니다.
- 전역 환경 레코드는 전역 변수를 관리하는 전역 스코프, 전역 객체의 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 제공합니다. 객체 환경 레코드와 선언적 환경 레코드로 구성되어 있습니다. 객체 환경 레코드는 기존의 전역 객체가 관리하던 var 키워드로 선언한 전역 변수와 함수 선언문으로 정의한 전역 함수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체를 관리합니다. 선언적 환경 레코드는 let, const 키워드로 선언한 전역 변수를 관리합니다. 위 예제에서 전역에 있는 a는 객체 환경 레코드에, b는 선언적 환경 레코드에 등록됩니다. foo 전역 함수도 객체 환경 레코드에 등록됩니다.
- 사실 객체 환경 레코드에 바로 a와 foo 전역 함수가 등록되는 것은 아닙니다. BindingObject라고 부르는 객체와 연결됩니다. 그리고 BindingObject는 전역 객체를 가리킵니다. 이 전역 객체에 a와 foo 함수가 프로퍼티로 등록됩니다. 이때 undefined로 바인딩되며 호이스팅이 일어나는 원인이 됩니다!
- b는 선언적 환경 레코드에 저장된다고 했습니다. 이때 일시적 사각지대에 빠지게 됩니다.
- 전역 환경 레코드에 [[GlobalThisValue]] 내부 슬롯이 존재합니다. 여기에 this가 바인딩됩니다. 전역 코드에서는 this는 전역 객체를 가리키므로 전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에는 전역 객체가 바인딩됩니다.
- 외부 렉시컬 환경에 대한 참조에는 null이 할당됩니다.
- 전역 코드가 순차적으로 실행되기 시작합니다.
- 변수 할당문이 실행돼서 전역 변수에 값이 할당됩니다. 그리고 foo 함수가 호출됩니다.
- foo 함수 코드 평가를 시작합니다.
- 함수 실행 컨텍스트가 전역 실행 컨텍스트 위에 푸시됩니다.
- 함수 렉시컬 환경이 생성됩니다.
- 함수 환경 레코드가 생성됩니다. 함수 환경 레코드는 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩 함수를 등록하고 관리합니다.
- 함수 환경 레코드의 [[ThisValue]] 내부 슬롯에 this가 바인딩됩니다. 바인딩 객체는 함수 호출 방식에 따라 결정되는데, 위 예시에서는 전역 객체가 바인딩됩니다.
- 외부 렉시컬 환경에 대한 참조에서는 foo 함수 정의가 평가된 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경이 바인딩됩니다. 즉, 위 예시에서는 전역 렉시컬 환경이 바인딩됩니다.
- foo 함수 코드가 실행되기 시작합니다.
- 변수 할당문이 실행돼서 값이 할당됩니다.
- foo 함수 코드 실행이 종료됩니다. foo 함수 실행 컨텍스트가 팝되면서 제거됩니다.
- 전역 코드 실행이 종료됩니다. 전역 실행 컨텍스트도 팝되면서 실행 컨텍스트 스택에는 아무것도 남아있지 않게 됩니다.
'JAVASCRIPT > 자바스크립트 이론' 카테고리의 다른 글
브라우저 렌더링 과정 (0) | 2021.01.31 |
---|---|
클로저란 (0) | 2021.01.29 |
자바스크립트에서 this는 어떻게 바인딩될까 (5) | 2021.01.26 |
프로토타입이란 (0) | 2021.01.26 |
var 키워드와 let, const 키워드의 차이점 (2) | 2021.01.25 |