이력
- 2022.03.14 포스팅
- 2022.03.15 수정
- 2022.03.16 수정
모든 내용을 상세히 담으려 하지 않았습니다.
제가 모르는 부분이나, “이런 것도 있었어?” 하는 부분, 중요하다고 생각되는 부분,
다시 한번 되새기고 싶은 부분만 기록했습니다.
읽어주셔서 감사합니다.
25.1 실행 콘텍스트 생성 기준#
- EC는 실행가능 코드를 만났을 때 생성됩니다.
- 실행가능 코드에는 3가지 유형이 있습니다. 함수 코드(함수), 글로벌 코드(글로벌 오브젝트 함수), eval 코드(eval 함수) 입니다.
- 유형별로 EC가 처리하는 환경과 방법이 다릅니다. 함수코드는 LE에서, 글로벌 코드는 글로벌 환경(글로벌 오브젝트를 위한 렉시컬 환경)에서 수행됩니다. eval 코드는 LE가 아닌 동적 환경에서 수행되며 별도의 영역입니다.
25.2 실행 콘텍스트와 상태 컴포넌트#
- ES5 기준으로 EC 생성 시 3가지 상태 컴포넌트를 생성합니다.
- 렉시컬 환경 컴포넌트 (Lexical Environment Component)
- 변수 환경 컴포넌트 (Variable Environment Component)
- this 바인딩 컴포넌트 (This Binding Component)
- music() 함수가 실행되었을 때 EC의 상태 컴포넌트를 형상화 한 것입니다.
1
2
3
4
5
6
7
8
| function music() {}
music();
music EC = {
LEC: {},
VEC: {},
TBC: {}
}
|
25.2.1 LEC와 VEC#
- EC가 생성되면 LEC와 VEC를 생성합니다. 그리고 LE를 생성하여 VEC와 LEC에 첨부합니다.
- 아직 함수 안의 코드를 생성하지 않았기 때문에 LE의 ER은 빈 상탱며 OLER에는 실행중인 함수의 [[Scope]]를 설정합니다.
- 함수의 초기화 단계에서 변수 이름, 함수 선언문, arguments 오브젝트, 매핑한 파라미터를 LEC와 VEC에 설정하므로 두 컴포넌트의 프로퍼티가 같습니다.
- 실행 단계에서는 LEC를 사용하고, VEC는 초기값 환원 용도로 사용됩니다.
25.2.2 TBC#
- 호출한 함수가 속한 오브젝트가 TBC에 설정됩니다. 참조하고 있기 때문에 변화를 동적으로 알 수 있습니다.
25.3 스택#
- 스택은 EC의 논리적 구조이며 코드가 실행되는 곳입니다.
25.4 글로벌 코드 실행 콘텍스트#
- js 렌더링이 끝나면 다음의 절차로 글로벌 코드를 실행하기 위한 준비를 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| 1. 글로벌 환경을 생성합니다.
2. ER을 생성하여 글로벌 환경에 첨부합니다.
3. OER을 생성하여 ER에 첨부합니다.
- 빌트인 글로벌 오브젝트를 OER에 바인딩하기 때문에 NaN, Undefined
Infinity, eval, parseInt 등을 사용할 수 있게 됩니다.
4. OLER를 생성하여 글로벌 환경에 첨부합니다.
- 외부 렉시컬 환경이 없으므로 null입니다.
- (저자생각) OLER에 null이 설정되므로 isNaN 함수를 실생하려면 EC를 빠져나가 빌트인 글로벌
오브젝트에서 찾아야 합니다. 그런데 EC를 빠져나가면 종료되므로, EC안에서 찾기 위해 OER에
바인딩하는 것으로 생각됩니다.
5. 글로벌 오브젝트를 생성합니다.
- 글로벌 코드가 글로벌 오브젝트에 설정됩니다.
6. EC를 생성하고 엔진이 실행 컨텍스트 안으로 이동합니다.
- 글로벌 코드를 파라미터 값으로 EC에 넘겨줍니다.
7. LEC를 생성합니다.
8. 생성한 글로벌 환경을 LEC에 첨부합니다.
9. VEC를 생성합니다.
10. 생성한 글로벌 환경을 VEC에 첨부합니다.
11. TBC를 생성합니다.
12. 생성한 글로벌 오브젝트를 TBC에 설정합니다.
- 따라서 this.XXX를 통해 글로벌 오브젝트의 변수와 함수를 사용할 수 있게 됩니다.
|
25.5 글로벌 코드 실행#
- 글로벌 코드는 OER에 글로벌 코드를 해석하고 실행한 결과를 바인딩합니다. 아래에 표시된 디버깅 정보가 OER의 프로퍼티를 표시한 것입니다.
1
2
3
4
5
6
| debugger;
var aValue = 123;
function aSports() {
};
var aMusic = function() {};
aSports();
|
- js 엔진이 EC에 들어가면
- 앞절에서 다루었던 준비 단계를 수행하여 실행환경을 만듭니다.
- 글로벌 코드를 한줄씩 읽어나가며 함수 선언문을 function 오브젝트로 생성하고 OER에 설정합니다.
- 글로벌 코드의 첫번째 줄로 이동하여 작성한 순서대로 변수에 undefined를 할당하고 OER에 바인딩합니다.
- 글로벌 코드의 첫번째 줄로 이동하여 하나씩 실행하며 결과를 OER에 설정합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| 함수 선언문의 초기화
1. 엔진이 글로벌 코드의 첫 줄로 이동합니다.
2. 읽어가며 선언문을 찾습니다.
3. function aSports(){} 에서 Function 오브젝트를 생성합니다.
4. Function 오브젝트의 [[Scope]]에 글로벌 환경을 설정합니다.
- 글로벌 코드에서 함수를 호출했을 때 호출된 함수의 OLDE에 설정하게 됩니다.
5. Function 오브젝트의 [[Code]]에 함수 코드를 설정합니다.
- 글로벌 코드에서 함수를 호출했을 때 EC에 파라미터로 넘겨줍니다.
6. OER에 aSport Function 오브젝트를 설정합니다.
- 시행중에 aSports()를 만나면 OER에서 식별자가 aSports이고 값 타입이 Function 오브젝트인
프로퍼티를 찾아 호출합니다.
7. 다 찾으면 첫 줄로 다시 이동합니다.
변수, 함수 표현식 초기화
8. 순서대로 읽어가며 변수를 찾습니다.
9. aValue를 OER에 바인딩하고 값을 undefined로 설정합니다.
10. aMusic을 OER에 바인딩하고 값을 undefined로 설정합니다.
11. 다 찾으면 다시 첫 줄로 이동합니다.
글로벌 코드 실행
12. 순서대로 읽어가며 실행합니다. dubugger에서 멈춥니다.
13. aValued에 123을 할당한다.
- LEC: {
LE: {
ER: {
OER: {
aValue: 123
}
},
OLER: null,
}
}
14. function aSports(){} 문장은 건너 뛰고 var aMusic = function(){}; 으로 이동하여 Function
오브젝트를 생성해 aMusic에 할당합니다.
15. aSports()를 만나 호출합니다.
|
- 지금까지 글로벌 코드를 실행하기 위한 준비 단계, 함수 선언문 초기화, 함수 표현식/변수 초기화, 글로벌 코드 실행 단계까지 알아 보았습니다. 이제 aSports() 함수를 호출 했을 때인 함수 코드에 대한 과정을 살펴 보겠습니다.
25.6 함수 코드 실행 콘텍스트#
- 함수 코드를 실행하기 위한 준비 단계, 함수 선언문 초기화, 함수 표현식/변수 초기화, 함수 코드 실행 단계로 나눠 살펴봅니다.
- 글로벌 코드와 함수 코드의 차이점은
- 글로벌 코드는 글로벌 환경이고, 함수 코드는 렉시컬 환경
- 함수 코드는 OLER에 값이 있습니다.
- 함수 코드는 DER에 함수코드의 해석 및 결과를 바인딩
- 함수 코드는 주고 받는 파라미터가 있습니다.
1
2
3
4
5
| var obj = {}
obj.music = function(one, two) {
return one + two;
}
obj.music(10, 20);
|
- music을 호출하는 시점에 아래의 항목이 이미 준비되어 있으며 엔진이 EC로 들어갈 때 파라미터 값으로 갖고 가는 항목입니다.
- 호출한 함수가 속한 오브젝트
- 호출한 함수의 파라미터 값
- 호출된 함수 코드
25.7 실행 콘텍스트와 환경 설정#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| 함수 코드 실행 환경 설정
1. EC를 생성합니다.
2. 3개의 파라미터를 갖고 EC안으로 들어갑니다.
3. LEC를 생성합니다.
4. VEC를 생성합니다.
5. TBC를 생성합니다.
6. 호출한 함수가 속한 오브젝트의 프로퍼티를 TBC에 설정합니다.
7. 새로운 LE를 생성합니다.
- 오브젝트 인스턴스로 생성하면 프로퍼티를 공유하게 되지만 빌트인 형태의 렉시털 환경을 사용하여
인스턴스로 생성합니다.
8. ER을 생성하여 LE에 첨부합니다.
9. DER을 생성하여 ER에 첨부합니다.
- 아직 함수 코드를 해석하지 않았으므로 빈 상태입니다.
10. OLER을 생성하여 LE에 첨부합니다.
11. 호출한 함수 오브젝트의 [[Scope]]를 OLER에 설정합니다.
12. LE를 LEC에 설정합니다.
13. LE를 VEC에 설정합니다.
|
25.8 파라미터 값 매핑#
- sports.get(1, 2)와 같이 get 함수를 호출하면 파라미터 값을 EC의 파라미터로 넘겨줍니다. 그리고 sports 오브젝트의 [[FormalParameters]]에 호출된 함수의 파라미터 이름이 작성되어 있습니다.
- 값과 이름을 매핑한 파라미터는 DER에 설정합니다.
- 함수 코드의 첫 줄은 해석하기전에 DER에 설정하므로 변수 초기화 단계에서 같은 이름의 변수를 사용하더라도 값이 변경되지 않습니다.
25.9 함수, 변수 바인딩#
- 파라미터를 DER에 설정한 이후에는 다음의 처리를 거칩니다.
- 모든 함수 선언문을 Function 오브젝트로 생성하여 DER에 설정합니다.
- arguments 오브젝트를 DER에 바인딩합니다.
- 모든 변수와 함수 표현식을 DER에 바인딩합니다.
25.10 eval 코드와 실행 콘텍스트#
- 함수는 Function 오브젝트를 생성한 후, 다른 문장에서 생성한 Function 오브젝트를 호출합니다. 함수 생성과 호출이 구분되어 있습니다. Function 오브젝트를 생성할 때 EC에서 사용할 [[Scope]], [[Code]]를 준비합니다. 반면 eval 코드는 엔진이 eval 함수(인터프리터 방식이라 느리고 보안에도 취약하다)를 만나면 바로 실행합니다. EC에 사용할 값은 사전에 준비할 수 없으므로 실행시점에 준비합니다. eval 함수는 자신을 호출한 함수의 EC를 사용하여 EC에 환경을 설정합니다.
1
2
3
4
5
6
7
8
9
| var one = 11;
funcion sports() {
var two, code, result;
two = 22;
code = "function soccer(){return this.one + two}; soccer()";
result = eval(code);
js.log(result);
}
window.sports();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| sports 함수 실행 환경 설정
1. 함수가 호출되어 EC를 생성합니다.
2. 3개의 컴포넌트를 생성합니다.
3. TBC에 호출한 함수가 속한 오브젝트(window 이므로 글로벌 오브젝트)를 설정합니다.
4. 새로운 LE를 생성합니다.
5. ER과 OLER을 생성해 LE에 첨부합니다.
6. DER을 생성해 ER에 첨부합니다.
7. 호출한 함수의 [[Scope]]를 OLER에 설정합니다.
- 글로벌 환경이 설정됩니다.
8. 생성한 LE를 LEC와 VEC에 설정합니다.
sports 함수 초기화, 실행
9. 초기화 단계에서 DER에 바인딩하고 초기값을 설정합니다.
- var two, code, result;
10. 실행 단계에서 DER에 값을 할당합니다.
11. eval 코드를 실행합니다.
|
- 11단계까지 sports EC에 아래와 같이 설정되어 있습니다.
1
2
3
4
5
| sports 실행 컨텍스트 상태
o LEC의 DER
o LEC와 VEC의 OLER
o VEC의 DER
o TBC
|
1
2
3
4
5
6
7
8
9
10
11
12
| eval 코드 실행 환경 설정
1. 문자열을 코드로 간주하여 해석합니다.
2. EC를 생성합니다.
3. EC안으로 이동합니다.
o eval 함수를 호출할 때의 EC가 오브젝트(obj) 파라미터 값이 됩니다.
o eval 함수에 작성한 문자열이 코드 파라미터 값이 됩니다.
4. LEC를 생성합니다.
5. 파라미터로 받은 obj의 LEC를 LEC에 설정합니다.
6. VEC를 생성합니다.
7. obj의 VEC를 VEC에 설정합니다.
8. TBC에 설정합니다.
9. obj의 TBC를 TBC에 설정합니다.
|
25.11 try-catch와 실행 콘텍스트#
1
2
3
4
5
6
7
8
9
| function sports() {
try {
var check = member;
} catch (e) {
debugger;
js.log(e.message);
return 'catch 수행';
}
}
|
- 그림에서 확인할 수 있듯이 sports EC가 스택의 가장 위에 있습니다. catch 블록이 별도의 EC를 생성하지 않고 sports EC에서 실행된다는 것입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| catch 블록에서 코드 처리
1. 새로운 EC를 생성하지 않습니다.
- 현재 sports EC가 스택의 가장위에 있습니다.
2. sports EC의 LEC 사본을 만듭니다.
- catch 문을 완료했을 때 원래 상태로 복원하기 위해서입니다.
3. 새로운 LEC를 생성합니다.
4. LE를 생성해 LEC에 첨부합니다.
5. ER을 생성해 LE에 첨부합니다.
6. DER을 생성해 ER에 첨부합니다.
- catch 블록의 코드를 바인딩하지 않습니다. 따라서 빈 오브젝트입니다.
7. OLER을 생성해 LE에 첨부합니다.
8. sports EC의 LEC를 OLER에 설정합니다.
9. 파라미터 이름으로 Error 오브젝트를 DER에 설정합니다.
- catch (e) { ... }
10. copyLEC를 LEC에 설정합니다.
11. catch 블록의 코드를 설정합니다.
12. catch 블록에서 반환한 값을 임시 영역에 저장합니다.
13. copyLEC를 sports LE에 설정해 복원한다.
14. 임시로 저장한 값을 반환한다.
|