JavaScript를 이해하기 위해 필수적인 개념인 실행 컨텍스트에 대해 알아보자.
아래의 글은 참고링크를 해석한 글이다.
실행 컨텍스트는 자바스크립트 코드가 evaluated되고 executed 되는 환경에 대한 추상적인 개념이다.
JS 코드가 구동될 때, 항상 그 코드는 실행 컨텍스트 안에서 작동하게 된다.
Simply put, an execution context is an abstract concept of an environment where the Javascript code is evaluated and executed. Whenever any code is run in JavaScript, it’s run inside an execution context.
Types of Execution Context
실행 컨텍스트에는 3가지의 종류가 존재한다.
- Global Execution Context —
- default / 기본 실행 컨텍스트 이다.
- 어떤 함수 안에도 위치하지 않는 코드들은 global execution context안에 위치하게 된다.
- global object, (브라우저에서는 window object)를 생성하고 this의 value를 global object와 같게 만든다.
- 하나의 프로그램 안에는 오직 한 개의 global executino context만 존재할 수 있다.
- Functional Execution Context —
- 함수가 실행될 때마다, 그 함수에 대한 새로운 execution context가 생성되게 된다.
- 각각의 함수는 각각 고유의 execution context를 가지게 되지만, 이 execution context는 함수가 실행될 때(즉, 호출될 때) 생성된다.
- 수많은 function exection contexts가 존재할 수 있다. 언제든 새로운 execution context가 생성될 때 마다, 정해진 일련의 과정을 거치게 되는데 이 절차에 대해서는 아래에서 설명한다.
Eval Function Execution Context— eval 함수안에서 실행된 code는 고유의 execution context를 가진다. 하지만, eval 함수는 절대 사용하면 안되므로, 여기서 다루지 않는다.
Execution Stack
Execution Stack(callstack) 은 stack 구조를 가진다(Last In First Out).
JS엔진이 처음 스크립트 코드를 만나게 되면, 가장 먼저 global excution context를 생성하고, 해당 context를 current execution stack에 집어 넣는다(push). JS엔진이 스크립트를 읽다가 함수가 호출된 부분을 만나게 되면, 엔진은 매번 새로운 function execution context을 생성하고, Execution stack의 가장 위에 집어 넣고(push), 그 아래에 있던 함수가 실행되게 된다.
JS엔진은 execution context의 가장 위에 있는 함수를 실행한다. 그리고 해당 함수의 실행이 끝나게 되면, stack의 가장 위에 있는 함수, 방금 실행이 끝난 함수,를 stack에서 제거한다.(pop). 모든 코드가 다 실행되고 나면, JS엔진은 global execution context를 현재 stack에서 제거한다.
아래 그림은 예시코드를 통해, 어떻게 실행스택이 stacked되고, 사라지는지 보여준다.
let a = 'Hello World!';function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}function second() {
console.log('Inside second function');
}first();
console.log('Inside Global Execution Context');
How is the Execution Context created?
위에서 우리는 JS엔진이 어떻게 execution context를 관리하는지 알아봤다. 이제, 어떻게 execution context가 실질적으로 JS엔진에 의해 생성되는지 알아보자.
execution context는 두가지 단계를 가지게 된다.
1) Creation Phase and 2) Execution Phase.
The Creation Phase
실행 컨텍스트는 creation phase에서 생성되며, 실행컨텍스트는 아래의 컴포넌트들을 가지게 된다.
- LexicalEnvironment component is created.
- VariableEnvironment component is created.
따라서, execution context는 개념적으로 아래처럼 표시 될 수 있다.
ExecutionContext = {
LexicalEnvironment = <ref. to LexicalEnvironment in memory>,
VariableEnvironment = <ref. to VariableEnvironment in memory>,
}
Lexical Environment(어휘적 환경)
ES6 공식문서에서는 Lexical Environment(어휘적 환경)을 아래와 같이 정의하고 있다.
A Lexical Environment 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. A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment.
쉽게 말해서 어휘적 환경은 identifier와 그 값을 mapping해주는 structure이다.
(여기에서 identifier란, 변수와 함수 이름, 그리고 실제 객체를 참조하는 변수를 의미한다, 객체구조처럼 키와 그 값을 이어주는 구조라고 생각하면 편하다.)
예를 들어, 아래의 코드를 살펴보자
var a = 20;
var b = 40;
function foo() {
console.log('bar');
}
위의 코드에서 생성된 어휘적 환경은 아래와 같을 것이다.
lexicalEnvironment = {
a: 20,
b: 40,
foo: <ref. to foo function>
}
그리고 이러한 어휘적환경은 세가지 컴포넌트를 가지게 된다.
- Environment Record
- Reference to the outer environment,
- This binding.
Environment Record
environment record는 변수와 함수의 선언이 lexical environment 안에 저장되는 공간을 의미하며,
이러한 environment record에는 두가지 타입이 있다.
- Declarative environment record(선언적 환경 레코드) — 이름에서 알 수 있듯이, 함수와 변수의 선언을 저장한다. 함수를 위한 어휘적 환경은 선언적 환경 레코드를 포함한다.
- Object environment record(객체 환경 레코드) —전역코드에 대한 lexical environment은 객체 환경 레코드를 포함한다. 변수와 함수의 선언 외에도, 객체 환경 레코드는 global binding object(browser환경에서 window 객체)도 저장한다. 각각의 binding object의 속성들에 대해 새 항목들이 객체환경 레코드에서 생성된다.
Note — 함수 코드에 있어서, 환경 레코드는 arguments 객체를 가지게 된다. 이 arguments 객체란, index와 해당 함수에 전달되는 arguments들을 맵핑하는 것과, arguments의 길이에 대한 정보를 가지고 있는 객체를 의미한다. 예를 들어 아래의 함수에서 arguments객체는 다음과 같을 것이다.
function foo(a, b) {
var c = a + b;
}
foo(2, 3);// argument object
Arguments: {0: 2, 1: 3, length: 2},
Reference to the Outer Environment(외부 환경에 대한 참조)
reference to the outer environment(외부 환경에 대한 참조)는 lexical environment 바깥쪽에 접근권한이 있음을 의미한다. 즉, JS 엔진은 현재의 lexical environment에서 변수를 찾지 못한다면 outer 환경에서 해당 변수를 찾을 수 있다는 것이다.
This Binding
This Binding 컴포넌트에서 this의 값이 결정된다.
global execution context에서, this의 값은 global object를 의미한다.(browser에서는 window 객체가 될 것이다.)
문제가 되는 것은 function execution context에서다. function execution context에서는 함수가 호출되는 환경에 따라 this의 값은 달라지게 된다. (함수가 선언된 환경이 아님에 주의하자.)
만약, 함수가 object refrence에 의해 호출된다면, this의 값은 해당 object가 되게 된다. 그렇지 않다면 this의 값은 global object(window) 혹은 undefined가 되게 되는 것이다.
const person = {
name: 'peter',
birthYear: 1994,
calcAge: function() {
console.log(2018 - this.birthYear);
}
}
person.calcAge();
// 'this' refers to 'person', because 'calcAge' was called with
//'person' object reference
const calculateAge = person.calcAge;
calculateAge();
// 'this' refers to the global window object, because no object reference was given
추상적으로, lexical environment는 아래와 같다.
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
}
outer: <null>,
this: <global object>
}
}
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
}
outer: <Global or outer function environment reference>,
this: <depends on how function is called>
}
}
Variable Environment
변수 환경은 EnvironmentRecord가 this execution context안에서 변수선언에 의해 생성된 bindings을 보유하는 어휘 환경을 의미한다.
이미 설명 한 것처럼, 변수환경 역시 어휘 환경이다. 따라서, 변수환경은 위에서 정의한 어휘환경의 모든 속성과 컴포넌트들을 가지고 있다.
ES6에서, 어휘환경과 변수환경이 다른 점은
어휘 환경은 함수선언, 변수 바인딩을 저장하는데 사용되는 반면,
변수환경은 var binding만을 저장한다는 것이다.
Execution Phase
이 단계에서 모든 변수들에 대한 할당이 마무리 되고, 마침내 코드가 실행되게 된다.
Example
Let’s look at some example to understand the above concepts:
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
위의 코드가 실행될 때, JS 엔진은 global code를 실행시키기 위해서 global execution context를 생성한다.
따라서, creation phase에서 global execution context는 아래와 같게 된다.
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>,
ThisBinding: <Global Object>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
c: undefined,
}
outer: <null>,
ThisBinding: <Global Object>
}
}
실행단계에서는, 변수 할당이 마무리되어 있다. 따라서, 실행단계에서의 global execution context는 아래와 같다.
GlobalExectionContext = {LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
a: 20,
b: 30,
multiply: < func >
}
outer: <null>,
ThisBinding: <Global Object>
},VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
c: undefined,
}
outer: <null>,
ThisBinding: <Global Object>
}
}
함수호출 부분, multiply(20, 30),을 만났을 때, 함수코드를 실행하기 위해 새로운 함수실행context가 생성되게 된다.
따라서, 생성단계에서 함수실행context는 아래와 같을 것이다.
FunctionExectionContext = {LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>,
},VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
g: undefined
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>
}
}
이후, 실행컨텍스트는 함수내부의 변수에 대한 할당이 끝났음을 의미하는 실행 단계를 거친다.
따라서, 실행단계에서의 함수실행컨텍스트는 아래와 같은 모습을 보일 것이다.
FunctionExectionContext = {LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>,
},VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
g: 20
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>
}
}
함수가 종료된 후, 리턴값은 c에 저장되게 된다. 그리고 그때 global lexical environment는 업데이트 되게 된다.
업데이트 이후, globalcode는 실행완료되고, 프로그램은 종료되게 된다.
'JavaScript' 카테고리의 다른 글
Mixed Content 문제 해결하기(https에서 http 사이트로 요청) (1) | 2022.06.15 |
---|---|
Hoisting(var, let, const, function ) (0) | 2022.05.23 |
json [object Object] 출력 (0) | 2022.04.13 |
new Set으로 배열 중복값 제거(with 퍼포먼스 테스트) (0) | 2022.04.11 |
[ES6] Map vs Set (0) | 2022.04.11 |