실행 컨텍스트를 배우기 앞서, 실행 컨텍스트를 구성하는 컴포넌트 중 하나인 *렉시컬 환경의 '환경 레코드(Environment Record)'를 알아보자.
*렉시컬 환경(Lexical Environment): 식별자와 값을 관리하고, 외부 상위 스코프에 대한 참조를 기록하는 데 사용하는 자료구조. 환경 레코드와 외부 렉시컬 환경 참조에 대한 정보로 구성되어 있다.
환경 레코드(Environment Record)
환경레코드란?
Environment Record is a specification type used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of ECMAScript code. — ECMAscript
ECMAscript 스펙에 따르면 환경 레코드는 렉시컬 중첩 구조 기반으로 변수, 함수 식별자와 값을 관리하는 데 사용된다. 환경 레코드는 함수 선언, 블록문, 또는 Try문의 Catch 절 등의 구문이 평가될 때마다 해당 코드로 생성된 *식별자 바인딩을 기록하기 위해 새 환경 레코드가 생성된다.
*식별자 바인딩: 식별자와 값의 연결.
코드가 평가될 때마다 렉시컬 환경은 실행 컨텍스트를 구성하는 또 다른 요소인 변수 환경 컴포넌트를 위해 새롭게 생성된다. 환경레코드는 렉시컬 환경을 구성하는 컴포넌트 중 일부이며, 환경 레코드는 이 렉시컬 중첩 구조에서 외부 환경 레코드를 참조하기 위한 [[OuterEnv]] 필드를 사용한다.
외부 환경 레코드는 여러 개의 내부 환경 레코드로부터 참조될 수 있다. 예를 들어, 함수 선언에 두 개의 중첩 함수 선언이 포함된 경우 각 중첩 함수의 환경 레코드는 자신이 정의된 함수의 환경 레코드를 외부 환경 레코드로 한다.
환경 레코드의 Type Hierarchy
환경 레코드는 '선언적 환경 레코드(Declarative Environment Record)', '객체 환경 레코드(Object Environment Record)', '전역 환경 레코드(Global Environment Record)' 3개의 하위 클래스를 갖는 추상 클래스이다.
그리고 선언적 환경 레코드는 다시 '함수 환경 레코드(Function Environment Record)'와 '모듈 환경 레코드(module Environment Record)'로 나뉜다.
•
환경 레코드(Abstract Class)
◦
선언적 환경 레코드
▪
함수 환경 레코드
▪
모듈 환경 레코드
◦
객체 환경 레코드
◦
전역 환경 레코드
환경 레코드 추상 클래스는 바인딩 여부, 바인딩 생성 및 삭제 등 식별자의 바인딩을 관리하는 데 필요한 메서드들을 정의하도록 한다.
선언적 환경 레코드(Declarative Environment Record)
선언적 환경 레코드는 var, const, let, class, module, import, function 등으로 스코프 내에서 선언된 식별자들의 바인딩을 관리한다.
•
함수 환경 레코드(Function Environment Record)
함수의 최상위 스코프를 나타내는데 사용되는 선언적 환경 레코드이다. 화살표 함수가 아니라면 this 바인딩을 제공한다. 또, 화살표 함수가 아니고 super를 참조하는 경우 super 메서드를 실행하는데 필요한 state를 제공한다.
•
모듈 환경 레코드(module Environment Record)
Module의 외부 스코프를 나타낼 때 사용하는 선언적 환경 레코드이다. 변경 가능, 변경 불가능한 바인딩과 더불어 변경 불가능한 import 바인딩(immutable import binding)을 제공한다. immutable import binding은 참조하는 외부 레코드의 바인딩에 대해서 간접적으로 접근해 변경하지 못하게 한다. 선언적 환경 레코드의 메서드를 전부 구현하지만, GetBindingValue나 DeleteBinding 같이 바인딩에 직접적으로 접근하는 일부 메서드는 구현 스펙이 다르다.
객체 환경 레코드
객체 환경 레코드는 Binding Object라는 객체와 연결된다. Binding Object의 프로퍼티 이름에 문자열 타입의 식별자 이름들을 바인딩한다.
*with 명령문으로 생성된 객체 환경 레코드는 Binding Object를 암묵적으로 제공하여 블록문 내에서 프로퍼티에 바로 접근할 수 있다.
*with: Math 객체의 프로퍼티에 바로 접근할 수 있다. 그러나 코드의 모호성때문에 사용하지 않는 것을 권장한다. 아래 예시에서 개발자가 foo 객체 내부에 어떤 프로퍼티가 있는지 모르면, console.log(values)가 foo 객체의 values인지 파라미터의 values인지 알 수 없다.
// Math.PI -> PI
with (Math) {
console.log(PI * 2 * 2);
}
// 모호성 위반
const foo = {
values: [1, 2, 3]
}
function f(foo, values) {
with (foo) {
console.log(values);
}
}
JavaScript
복사
전역 환경 레코드
전역 환경 레코드는 *realm에서 처리되는 가장 바깥의 스코프를 나타내는 데 사용된다. 전역 환경 레코드는 빌트인 전역 객체, 전역 객체의 프로퍼티 및 스크립트 내의 모든 최상위 선언에 대한 바인딩을 제공한다. 즉, 이론상 하나의 레코드이지만, 실제로는 '객체 환경 레코드'와 '선언 환경 레코드'로 구성된다.
전역 환경의 객체 환경 레코드는 Realm Record의 전역 객체와의 바인딩을 관리한다. 또한 빌트인 전역 객체(windows나 global 등)의 바인딩, 전역에서 선언된 모든 코드에 대한 바인딩을 제공한다.
*Realm: 어떤 코드든 평가되기 전, realm에 연결되어야 한다. realm은 객체, 전역 환경, 전역 스코프에서 로드되는 모든 코드, 기타 state 등으로 구성된다.
References
•
ECMAscript 9.1 ~ 9.3 https://tc39.es/ecma262/#sec-environment-records