자바스크립트(이하 JS) 엔진은 실행 가능한 코드(Executable Code)를 만나면 그 코드를 평가(Evaluation)해서 실행 문맥(Executable Context)으로 만든다.
실행 문맥: 실행 가능한 코드가 실제로 관리되는 영역, 실행에 필요한 모든 정보를 컴포넌트 여러 개가 나누어 관리하도록 만들어져 있다.
컴포넌트는 렉시컬 환경(LexicalEnvironment) 컴포넌트, 변수 환경(VariableEnvironment) 컴포넌트, 디스 바인딩(ThisBinding) 컴포넌트이며, 렉시컬 환경 컴포넌트가 가장 중요하다.
(렉시컬 환경 컴포넌트와 변수 환경 컴포넌트는 타입이 같고 with문을 사용할 때를 제외하면 내부 값이 같으므로 통일해서 설명한다.)
실행 문맥의 의사 코드
|
ExecutionContext = {
// 렉시컬 환경 컴포넌트
LexicalEnvironment: {},
// 변수 환경 컴포넌트
VariableEnvironment: {},
// 디스 바인딩 컴포넌트
ThisBinding: null,
}
|
8.5.3 렉시컬 환경 컴포넌트의 구성
실행 문맥의 구성 요소인 렉시컬 환경 컴포넌트는 JS 엔진이 JS 코드를 실행하기 위해 자원
(함수 또는 블록의 유효범위 안에 있는 식별자와 그 결과 값)을 모아 둔 곳.
JS 엔진은 그 자원을 키와 값의 쌍으로 바인드해서 렉시컬 환경 컴포넌트에 기록한다.
렉시컬 환경 컴포넌트는 환경 레코드(Environment Record)와 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)컴포넌트로 구성되어 있다.
렉시컬 환경 컴포넌트의 의사 코드
|
//렉시컬 환경 컴포넌트
LexicalEnvironment : {
//환경 레코드
EnvironmentRecord: {},
//외부 렉시컬 환경 참조
OuterLexicalEnvironment Reference: {}
}
|
환경 레코드
JS 엔진이 JS 코드를 실행하기 위해 자원을 바인드 해서 기록한 곳이 렉시컬 환경 컴포넌트라고 설명했는데
정확히 말하면 렉시컬 환경 컴포넌트의 환경 레코드에 저장하는 것이다
정리: 환경레코드는 유효 범위 안에 포함된 식별자를 기록하고 실행하는 영역으로, JS 엔진은 유효 범위 안의 식별자와 결과값을 바인드해서 환경 레코드에 기록합니다.
외부 렉시컬 환경 참조
JS는 함수 안에 함수를 중첩해서 정의할 수 있는 언어이다.
따라서 JS 엔진은 유효범위 너머의 유효 범위도 검색할 수 있어야 한다.
(대충 그걸 외부 렉시컬 환경 참조가 해준다는 내용)
외부 렉시컬 환경 참조에는 함수를 둘러싸고 있는 코드가 속한 렉시컬 환경 컴포넌트의 참조가 저장된다.
중첩된 함수 안에서 바깥 코드에 정의된 변수에 접근 할 때, JS엔진은 외부 렉시컬 환경 참조를 따라 한 단계씩 렉시컬 환경을 거슬러 올라가 그 변수를 검색한다.
8.5.4 환경 레코드의 구성
렉시컬 환경 안의 식별자와 그 식별자가 가리키는 값의 묶음이 저장되는 영역이 환경 레코드라고 했다.
이 환경레코드는 선언적 환경 레코드(Declarative Environment Record)와 객체 환경 레코드(Object Environment Record)로 구성되어 있으며, 저장하는 값의 유형에 따라 쓰임새가 달라진다.
환경 레코드의 의사코드
|
// 환경 레코드
EnvironmentRecord : {
//선언적 환경 레코드
DeclarativeEnvironmentRecord: {},
//객체 환경 레코드
ObjectEnvironmentRecord: {}
}
|
선언적 환경 레코드: 실제로 함수와 변수, catch문의 식별자와 실행결과가 저장되는 영역
객체 환경 레코드: 실행 문맥 외부에 별도로 저장된 객체의 참조에서 데이터를 읽거나 쓴다.
(객체 전체의 참조를 가져와서 객체 환경 레코드의 bindObject라는 프로퍼티에 바인드하도록 만들어짐)
8.5.4 프로그램 실행과 실행 문맥
실행 문맥은 스택 구조로 관리되며, 실행 문맥은 프로그램 실행 중에 스택에 push되어 실행된다.
가장먼저 실행하는 코드는 전역 코드이며, 스택의 맨 아랫부분에는 항상 전역 코드를 실행하기 위한 실행 문맥(Execution Context)이 자리잡고 있다.
전역 코드 안에서 함수를 실행 시 그 함수를 실행하기 위한 실행 문맥을 스택에 push한다.
그리고 그 함수의 작업을 끝내고 함수를 호출한 부분으로 제어권이 돌아오면 스택에서 pop한다.
이 때 실행하는 함수가 중첩 함수라면 중첩 함수의 실행 문맥을 새로 만들어서 스택에 push한다.
(중첩 함수의 실행 문맥이 외부 함수의 실행 문맥 안에 중첩되지 않는다)
(마찬가지로 함수 안에 있는 코드를 실행하는 도중에 다른 함수를 호출하면 그 함수의 실행 문맥도 스택에 push.)
(재귀함수역시 마찬가지이다.)
위 설명처럼 함수의 실행 문맥은 호출될 때마다 스택에 push된다.
그리고 return문이 실행되어 제어권이 호출한 코드로 돌아가면 스택에서 pop된다.
그래서, 실행 문맥 스택을 호출 스택(call stack)이라는 이름으로 부릅니다.
'Language > Javascript' 카테고리의 다른 글
| Javascript의 전역 객체 (0) | 2020.07.03 |
|---|---|
| 변수안에 함수 정의, 대입, 호출 차이 (0) | 2020.05.13 |
| 점프문 (0) | 2020.05.02 |
| for/in문 (0) | 2020.05.01 |
| 객체 개념 박살내기 (0) | 2020.04.22 |