Execution Context는 자바스크립트 코어에서 가장 중요한 부분 중 하나인데, 구글링해보면 옛날 자료를 기반으로 한게 대부분입니다. (절반정도가 ES3...)
그래서 ECMAScript 명세와 여러 포스팅들을 종합해서 최신 자바스크립트(ES2022) Execution Context 동작을 정리하고자 했습니다.
틀린 부분은 댓글로 알려주시면 감사하겠습니다.
Execution Context
는 세 가지 경우에 생성된다Global Execution Context
: 글로벌 코드가 평가될 때(프로그램 시작)Functional Execution Context
: 함수가 호출될 때Eval Function Execution Context
: eval()
함수가 실행될 때Call stack size exceeded
라는 오버플로우 에러 메세지를 볼 수도 있다Execution Context
를 Running Execution Context
라고 부른다Execution Context가 중요한 이유는 스코프와 식별자 바인딩을 저장하는 Environment Record
를 참조하기 때문이라고 생각한다.
ECMAScript가 버전업을 하면서 Execution Context
내부 구조에 크고작은 변화들이 계속 있었는데, Environment Record
와 관련된 부분 위주로 간단하게 살펴보자.
Execution Context
내의 주요 컴포넌트this
바인딩LexicalEnvironment
VariableEnvironment
LexicalEnvironment
, VariableEnvironment
두 레코드가 같은 참조값을 가진다. (LexicalEnvironment
에 VariableEnvironment
값을 복사하여 할당)LexicalEnvironment
에 반영된다with
, catch
문을 평가할 때 새로운 Environment Record
를 생성해서 Lexical Environment
가 갱신된다.LexicalEnvironment
를 [[Scope]]
로 참조한다 VariableEnvironment
를 [[Scope]]
로 참조한다 Lexical Environment
에서 관리된다LexicalEnvironment
참조를 탐색한다Execution Context 내에 LexicalEnvironment라는 컴포넌트가 있고,
렉시컬 스코프를 구현하기 위한 outer 참조와 Environemnt Record를 가지는 Lexical Environment라는 명세가 따로 있다.
이름이 같아서 햇갈리므로 이 글에서는 띄워쓰기 유무로 구별하겠다.
with문이 있을 때 어떻게 되는지 그림으로 나타내보자.
아래와 같은 코드를 평가하면 foo, bar가 각각 10, 20을 출력한다.
var a = 10;
// FD
function foo() {
console.log(a);
}
with ({a: 20}) {
// FE
var bar = function () {
console.log(a);
};
foo(); // 10!, from VariableEnvrionment
bar(); // 20, from LexicalEnvrionment
}
foo(); // 10
bar(); // still 20
with
문 진입 전에는 다음과 같은 상황일 것이다.
with
문 진입하여 bar
함수 할당되고 난 후에는 다음처럼 Lexical Environment
가 하나 더 추가된 상황일 것이다.
foo
함수는 VariableEnvironment
를 스코프로 가지기 때문에 a 값이 10
이 되고, bar
함수는 LexicalEnvrionment
를 스코프로 가져서 a
값이 20이 된다.this
바인딩이 Environment Record
에서 관리된다Function Environment Record
타입이 등장하였고, 화살표 함수임을 나타내기 위한 프로퍼티가 추가되었다Execution Context
의 LexicalEnvironment
, VariableEnvironment
컴포넌트의 역할이 변경되었다VariableEnvironment
는 var
식별자의 바인딩을 저장한다LexicalEnvironment
는 그 외의 let
, const
, function
선언의 바인딩을 저장한다LexicalEnvironment
가 생성된다.블록 스코프를 가지는 let
, const
키워드가 추가되었다. 블록문이 있을 때 어떻게 되는지 예시를 보자.
var a = 1;
function Foo() {
const b = 2;
{
const b = 3;
console.log(a); // 1
console.log(b); // 3
}
}
Foo();
console.log()
가 실행될 때의 Execution Context와 Environment를 그려보면 다음처럼 될 것이다.
블록문이 새로운 Lexical Environment
를 생성하여 스코프 체인 앞에 추가한 것을 볼 수 있다. 그렇기 때문에 b
값이 3으로 출력되는 것이다.
Lexical Environment
가 없어지고 하던 역할을 Environment Reocrd
가 하게 된다Environment Record
의 [[OuterEnv]]
에서 상위 스코프를 참조하게 되었다Execution Context
에 PrivateEnvironment
가 추가되었다일반적인 함수 호출을 하는 경우 다음처럼 구성될 것이다.
그동안 공부하면서 식별자 값을 어떻게 정확하게 찾는지 개인적으로 궁금했었는데 스펙에 기술돼있었다. Execution Context
의 ResolveBinding
메소드가 바인딩된 값을 찾는 역할을 한다.
ResolveBinding(name, env?)
: 식별자 이름과 옵션으로 Environment Record
를 매개변수로 받는다. env
가 없으면 Running Execution Context
의 LexicalEnvrionment
를 탐색한다. 탐색하는데 GetIdentifierReference
메소드를 호출한다.GetIdentifierReference(env, name, strict)
: Environment Record
의 메소드로 실제로 식별자 바인딩 값을 찾는 역할을 한다. env.[[OuterEnv]]
체인을 타고가면서name
의 바인딩을 찾아서 반환한다.자세한 설명은 https://tc39.es/ecma262/#sec-resolvebinding 참조
https://cabulous.medium.com/javascript-execution-context-lexical-environment-and-block-scope-part-3-fc2551c92ce0
https://poiemaweb.com/js-execution-context
https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0
https://meetup.toast.com/posts/129
http://dmitrysoshnikov.com/ecmascript/es5-chapter-3-2-lexical-environments-ecmascript-implementation/#structure-of-execution-context
https://dev.to/luigircruz/javascript-execution-context-38cn
https://262.ecma-international.org/12.0/#sec-execution-contexts
https://262.ecma-international.org/11.0/#sec-execution-contexts
https://262.ecma-international.org/5.1/#sec-10.3
https://tc39.es/ecma262/#sec-execution-contexts
깔끔한 정리 잘보고갑니다.