실행 컨텍스트 관련 첫번째 포스트인 자바스크립트 실행 컨텍스트(Execution Context) 에서는 실행 컨텍스트란 무엇인지 그리고 유형은 어떠한 것들이 있으며 Javascript 엔진이 실행 컨텍스트를 어떻게 관리하는지 알아보았습니다. 이번 포스트에서는 Javascript 엔진이 실행 컨텍스트를 어떻게 생성하며 실행 컨텍스트는 어떠한 구조로 이루어져 있는지 알아보겠습니다.
실행 컨텍스트의 물리적 구조를 설명하기에 앞서 일부 블로그의 내용이 ECMAScript의 ES5 이전의 개념으로 작성되어 있었습니다. 그래서 ES5 이전의 내용과 ES5 이후의 구조를 비교하기 위해 많은 블로그와 ECMAScript 의 공식 문서(ecma-262)의 Executable Code and Execution Contexts 내용을 참고하였고 용어 및 구조에 있어서 변경사항이 있었습니다.
ES5 이전과 이후
ES5 이전과 이후의 차이가 크진 않습니다. 그러나 최신 스펙으로 공부하고 알고 있는것이 맞다고 생각되었고 짧게나마 제가 공부하면서 알게된 변경사항에 대해 정리해보았습니다.
실행 컨텍스트를 실행하기 위해 보유하는 프로퍼티
- ES5 이전:
- 변수 객체 (Variable Object)
- Scope Chain
- this (Context Object)
- ES5 이후
- Lexical Environment
- Variable Environment
- Variable Object, Activation Object, Scope Chain 등의 개념이 Lexical Environment로 변경됨
실행 컨텍스트에서 “this”를 관리하는 주체
- ES5 이전: 실행 컨텍스트 객체가 직접
- ES5 이후: Lexical Environment
“Scope Chain”의 변화
Javascript는 Lexical Scope를 갖는 언어로써, 식별자 탐색을 위한 참조가 ES5 이후 아래와 같이 변경되었다.
- ES5 이전:
- Scope Chain
- ES5 이후
- Lexical nesting structure
- Logical nesting of Lexical Environment values
- Chain(사슬) 의 구조보다는 nesting(중첩) 의 의미를 강조에 의미를 둠
실행 컨텍스트와 그 구조
실행 컨텍스트 1편인 자바스크립트 실행 컨텍스트(Execution Context) 에서 정의했듯이 실행 컨텍스트란 “실행 가능한 코드를 형상화 하고 구분 하는 추상적인 개념으로 코드가 실행되고 있는 구역 또는 범위“ 로써 각 실행 컨텍스트는 아래와 같은 모양을 갖으며 이는 ES5 이전의 구조인 실행 컨텍스트 안에 3개의 프로퍼티(Variable Object, Scope Chain, this Object)를 갖는 구조와 다르다.
1. 실행 컨텍스트
그림의 가장 좌측에 그리고 최상위 개념인 실행 컨텍스트 는 “코드가 실행되고 있는 구역 또는 범위” 라는 정의에 맞게 아래와 같이 어느 영역에서 호출 및 실행되고 있느냐에 따라 구분할 수 있으며 각 컨텍스트에 따라서 하위 개념 및 구조 또한 달라진다.
- 전역(Global) 영역 에서 실행되고 있으면 전역 실행 컨텍스트
- 전역 컨텍스트에서 호출 및 실행된 함수(Function)의 영역 에서 실행되고 있으면 함수 실행 컨텍스트
그리고 모든 실행 컨텍스트는 아래와 같은 공통된 구조를 갖으며, 짧게 정의하자면, 변수의 참조를 기록하는 환경이라 할 수 있겠다.
1 | ExecutionContext = { |
위 두개의 프로퍼티는 초기화 시에는 같은 객체를 바라보고 있으며 실행 컨텍스트가 호출 및 실행 영역에 따라 구분되는 것과 마찬가지로 각 바라보는 또는 Linked Object가 달라진다.
- 전역(Global) 실행 컨텍스트 의 프로퍼티라면
- Global Lexical Environment / Global Variable Environment
- 일반 함수(Function) 실행 컨텍스트 의 프로퍼티라면
- Lexical Environment / Variable Environment
2. Lexical Environment
한글로 “어휘적 환경” 또는 “정적 환경”라고 할 수 있으며 Javascirpt 코드에서 변수 또는 함수 식별자를 맵핑(identifier-variable mapping)하는데 사용되는 객체로 아래와 같은 환경으로 구성되어 있다. (여기서 식별자(identifier) 란 참조 대상 식별자로써 변수나 함수의 이름을 참조하며 변수 는 함수 객체와 배열 객체를 포함한 실제 객체 또는 원시값에 대한 참조이다.)
- Lexical Environment
- Outer Environment Reference 또는 Reference to the outer environment
- Environment Record
2.1 Outer Environment Reference
식별자(identifier) 검색을 위해 외부 Lexical Environment를 참조하는 포인터로 중첩된 Javascript 코드에서 스코프 탐색을 위해 사용된다.
- Global Lexical Environment 의 Outer Environment Reference 값은
null
- 일반 Function 환경에서의 Lexical Environment 의 Outer Environment Reference 값은 해당 실행환경(Execution Context)의
상위 실행환경의 Lexical Environment
를 참조
2.2 Environment Record
현재 유효범위 내의 값에 식별자들의 바인딩을 기록하는 객체로 모든 지역 변수(변수, 함수 등)를 프로퍼티로 저장하며 this
와 같은 기타 정보도 여기에 저장된다.
현재 레코드 타입이 포함된 실행 컨텍스트(Execution Context)에 따라 Global Environment Record 와 일반 Function 환경에서의 Environment Record 으로 구분할 수 있으며 담고 있는 데이터에 대한 속성 또한 달라진다.
해당 Record 타입의 구조는 실행 컨텍스트의 영역을 불문하고 아래의 레코드 타입을 갖는다:
Environment Record
- Object Environment Record
- Declarative Environment Record
2.2.1 Object Environment Record
with 문과 같이 식별자(Identifier)를 특정 객체의 속성으로 취급할때 사용되며, 이를 위해 binding Object라는 속성으로 특정 객체를 가리킨다. 현재 레코드는 Environment Record를 상속한 서브 클래스이다.
전역(Global) 실행 컨텍스트의 Object Environment Record (in Environment Record) 는 브라우저 환경의 경우 전역 객체로서 window
객체를 저장한다. 따라서 일반적인 함수(Function) 실행 컨텍스트와 다르게 Object, Array, Function과 같은 built-in global 객체와 전역 코드에서의 함수/변수 선언에 의해 생성된 모든 식별자 정보를 저장하며 찾을 수 있다. (이는 함수 실행 컨텍스트의 Environment Record와 다른 점이다.)
2.2.2 Declarative Environment Record
이름에서 알 수 있듯이 변수 선언, 함수 선언, catch 절과 같은 문법요소의 효과 와 같은 정보를 정의하기 위해 사용된다. 즉, 식별자(Indentifier) 정보를 해당 레코드 타입에서 찾을 수 있다는 말이다. 함수 코드에 대한 lexical environment는 해당 레코드 타입을 포함하며 하위 레코드 타입으로는 아래와 같다.
- Declarative Environment Record
- Function Environment Record
- Module Environment Record
전역(Global) 실행 컨텍스트의 Declarative Environment Record (in Environment Record) 는 함수(Function) 실행 컨텍스트의 Declarative Environment Record와 다르게 전역 코드에서 object Environment Record에 포함되지 않은 식별자 정보만 보유한다. 왜냐하면 하단 전역 환경에서의 object Environment Record가 해당 레코드의 역할을 가져가 버렸기 때문이다.
3. Variable Environment
LexicalEnvironment 와 funtion, 변수 식별자가 binding 되는 점을 포함해 동일하다. VariableEnvironment 또한 Lexical Environment이다. 그러나 만들어진 변수 선언 및 함수 선언에 대해 바인딩을 유지한다.
- LexicalEnvironment는 코드 실행 중에 실행 컨텍스트 내에서 변경될 수 있지만 VariableEnvironment는 항상 값을 유지한다.
- LexicalEnvironment는 일시적으로 LexicalEnvironment 하위에 새로운 환경을 가리킵니다.
- 이 새로운 환경은 임시 바인딩을 보유합니다.
- 그리고 임시 범위를 벗어나면 VariableEnvironment가 참조하고 있는 값으로 LexicalEnvironment를 복구합니다.
ES6 에서 LexicalEnvironment와 VariableEnvironment 둘의 차이점은 전자가 함수 선언과 변수 (let
과 const
)의 바인딩을 저장하고 후자는 변수 var
만 저장한다.
4. 정리 - 실행 컨텍스트의 구조
실행 컨텍스트 하위 구조
1 | ExecutionContext = { |
Lexical Environment 구조
1 | Lexical Environment = { |
Variable Environment 구조
1 | EnvironmentRecord: { |
여기까지 ES5 이전과 이후의 실행 컨텍스트(Execution Context)의 변화와 구조에 대해 알아봤습니다. 다음 포스트에서는 실제 예제를 가지고 위의 실행 컨텍스트 구조가 어떻게 형성되고 실행되는지 알아보겠습니다.
참조
- Understanding Execution Context and Execution Stack in Javascript
- Executable Code and Execution Contexts - ecma-262 from ECMAScript
- [Javascript] Execution Context와 Lexical Environment - https://iamsjy17.github.io
- [JS] 자바스크립트의 The Execution Context (실행 컨텍스트) 와 Hoisting (호이스팅) - https://velog.io/@imacoolgirlyo
- 자바스크립트 함수 (3) - Lexical Environment - meetup.toast.com
- 자바스크립트의 스코프와 클로저 - meetup.toast.com
- 변수의 유효범위와 클로저 - https://ko.javascript.info