|
| 1 | +자바스크립트의 동작 원리를 담은 핵심 개념으로, 이를 바르게 이해하면 앞서 배운 자바스크립트 동작과 앞으로 배울 내용을 명확히 알 수 있습니다. |
| 2 | + |
| 3 | +## 소스코드의 타입 |
| 4 | +> ECMAScript가 정의한 4가지 소스코드 타입입니다. 이들 코드는 **실행 컨텍스트를 생성**합니다. |
| 5 | +
|
| 6 | +이렇게 구분하는 이유는 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문입니다. |
| 7 | +1. 전역 코드(전역에 존재하는 코드) |
| 8 | + - 전역 변수를 관리하기 위한 최상위 스코프인 전역 스코프를 생성합니다. |
| 9 | + - 전역 코드가 평가되면 전역 실행 컨텍스트가 생성됩니다. |
| 10 | +2. 함수 코드(함수 내부에 존재하는 코드) |
| 11 | + - 지역 스코프를 생성하고 지역 변수, 매개변수, arguments 객체를 관리합니다. |
| 12 | + - 함수 코드가 평가되면 함수 실행 컨텍스트가 생성됩니다. |
| 13 | +3. ~~eval 코드~~(사용을 권장하지 않습니다) |
| 14 | + - strict mode에서 자신만의 독자적인 스코프를 생성합니다. |
| 15 | + - eval 코드가 평가되면 eval 실행 컨텍스트가 생성됩니다. |
| 16 | +4. 모듈 코드 |
| 17 | + - 모듈별로 독립적인 모듈 스코프를 생성합니다. |
| 18 | + - 모듈 코드가 평가되면 모듈 실행 컨텍스트가 생성됩니다. |
| 19 | + |
| 20 | +이렇게 놓고 보면, 저희가 선택할 수 있는 타입은 대부분 두 가지가 됩니다. 바로 전역 코드와 함수 코드죠. 이것으로 실행 컨텍스트에 대해 알아 볼 거에요. |
| 21 | + |
| 22 | +<br> |
| 23 | + |
| 24 | +## 소스코드 평가와 실행 |
| 25 | +> 자바스크립트 엔진은 **소스코드 평가**와 **소스코드 실행** 과정으로 나누어 처리합니다. |
| 26 | +
|
| 27 | +소스코드 평가 과정은 이렇습니다. |
| 28 | +1. 실행 컨텍스트 생성 |
| 29 | +2. 변수, 함수 등의 선언문만 먼저 실행 |
| 30 | +3. 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록 |
| 31 | + - 여기서의 스코프는 렉시컬 환경의 환 경 레코드라고 합니다. |
| 32 | + |
| 33 | +소스코드 평가 과정이 종료되면 소스코드가 실행되는, `런타임`이 시작됩니다. |
| 34 | +1. 선언문을 제외한 소스코드가 순차적으로 실행됩니다. |
| 35 | +2. 소스코드 실행에 필요한 정보인 `변수`나 `함수`의 참조를 실행 컨텍스트가 관리하는 스코프에서 검색합니다. |
| 36 | + - 변수 값의 변경 등 소스코드의 실행 결과는 다시 실행 컨텍스트가 관리하는 스코프에 등록합니다. |
| 37 | + |
| 38 | +<br> |
| 39 | + |
| 40 | +## 실행 컨텍스트의 역할 |
| 41 | +> 전역 코드와 함수 코드로 예제를 살펴봅시다. |
| 42 | +
|
| 43 | +자바스크립트 엔진은 전역 코드 평가 - 실행, 함수 코드 평가 - 실행을 순차적으로 진행합니다. |
| 44 | +1. 전역 코드 평가 |
| 45 | + - 전역에 존재하는 변수와 함수 선언문을 먼저 실행하여 실행 컨텍스트의 **전역 스코프**에 등록됩니다. |
| 46 | + - var 키워드로 선언한 변수와 함수 선언문의 경우 전역 객체의 프로퍼티가 됩니다. |
| 47 | + - let, const 키워드로 선언한 변수는 보이지 않는 개념적 블록에 존재합니다. |
| 48 | + |
| 49 | +2. 전역 코드 실행 |
| 50 | + - 런타임이 시작됩니다. 전역 변수에 값이 할당되고 함수가 호출됩니다. |
| 51 | + - 함수가 호출되면 전역 코드 실행이 일시 중단되고 함수 내부로 진입하여 코드를 실행합니다. |
| 52 | + |
| 53 | +3. 함수 코드 평가 |
| 54 | + - 함수의 매개변수와 지역 변수 선언문을 먼저 실행하고 실행 컨텍스트의 **지역 스코프**에 등록됩니다. |
| 55 | + - 함수 내부에서 지역 변수처럼 사용 가능한 **arguments 객체가 생성되어 지역 스코프에 등록**되고 **this 바인딩도 결정**됩니다. |
| 56 | + |
| 57 | +4. 함수 코드 실행 |
| 58 | + - 런타임이므로 매개변수와 지역 변수에 값이 할당됩니다. |
| 59 | + - 함수 호출 코드가 존재한다면 함수를 호출한 식별자를 스코프 체인을 통해 검색합니다. |
| 60 | + - 전역 스코프까지 탐색하나 전역 스코프에 해당 함수가 존재하지 않다면 전역 객체의 프로퍼티를 전역 스코프처럼 검색합니다. |
| 61 | + - 호출한 함수 메서드를 식별자 객체의 프로토타입 체인을 통해 검색합니다. |
| 62 | + - 인수가 존재한다면 인수를 전달한 표현식을 평가하고, 각 식별자를 스코프 체인을 통해 검색합니다. |
| 63 | + - 함수 실행이 종료되면 함수 호출 이전으로 돌아가 전역 코드 실행을 계속합니다. |
| 64 | + |
| 65 | +이처럼 코드가 실행되려면 스코프를 구분해 식별자와 바인딩된 값이 관리되어야 하며, 중첩 관계에 의해 스코프 체인을 형성하고 식별자를 검색할 수 있어야 합니다. 또한 전역 객체의 프로퍼티도 전역 변수처럼 검색할 수 있어야 하죠. |
| 66 | + |
| 67 | +함수 호출이 종료되면 함수 호출 이전으로 돌아가므로 `현재 실행 중인 코드`와 `이전에 실행하던 코드`를 구분하여 관리해야 합니다. 즉 아래와 같은 조건이 생기죠. |
| 68 | +1. 선언에 의해 생성된 `모든 식별자(변수, 함수, 클래스 등)`를 스코프를 구분하여 등록하고 `상태 변화(식별자에 바인딩 된 값의 변화)를 지속적으로 관리`할 수 있어야 합니다. |
| 69 | +2. 스코프는 중첩 관계에 의해 `스코프 체인을 형성`해야 하며, 스코프 체인을 통해 상위 스코프로 이동하며 식별자를 검색할 수 있어야 합니다. |
| 70 | +3. 현재 실행 중인 코드의 실행 순서를 `변경(함수 호출에 의한 실행 순서 변경 등)`할 수 있어야 하며, `되돌아` 갈 수도 있어야 합니다. |
| 71 | + |
| 72 | +이 모든 것을 관리하는 게 실행 컨텍스트로 소스코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역입니다. 즉, **식별자(변수, 함수, 클래스 등)를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘이며, 모든 코드는 실행 컨텍스트를 통해 실행됩니다**. 식별자와 스코프는 실행 컨텍스트의 **렉시컬 환경(Lexical Enviroment)** 으로 관리하고, 코드 실행 순서는 **실행 컨텍스트 스택**으로 관리합니다. |
| 73 | + |
| 74 | +아래 코드로 실행 컨택스트 스택에 대해 자세히 알아봅시다. |
| 75 | + |
| 76 | +<br> |
| 77 | + |
| 78 | +## 실행 컨텍스트 스택 |
| 79 | +> 자바스크립트 엔진은 실행 컨텍스트를 **스택(Stack) 자료구조**로 관리하며, 이를 실행 컨텍스트 스택이라고 합니다. |
| 80 | +
|
| 81 | +코드와 함께 시작합니다. |
| 82 | + |
| 83 | +```js |
| 84 | +// 1. 전역 코드 : 전역 변수 선언 |
| 85 | +const x = 1; |
| 86 | +const y = 2; |
| 87 | + |
| 88 | +// 전역 코드 : 함수 정의 |
| 89 | +function out(){ |
| 90 | + // 2. 함수 코드 : 지역 변수 선언 |
| 91 | + const x = 10; |
| 92 | + |
| 93 | + function in(){ |
| 94 | + const y = 20; |
| 95 | + console.log(x + y); |
| 96 | + } |
| 97 | + in(); |
| 98 | +} |
| 99 | + |
| 100 | +out(); |
| 101 | +``` |
| 102 | + |
| 103 | +위 코드를 실행하면 순차적으로 실행 컨텍스트 스택가 추가(push)되거나 제거(pop)됩니다. 도식화하면 아래와 같아요. |
| 104 | + |
| 105 | +<br> |
| 106 | + |
| 107 | +<div align='center'> |
| 108 | + |
| 109 | +<img src='./img/execution_context/execution_context_stack.jpg' width='600'/> |
| 110 | + |
| 111 | +<br> |
| 112 | + |
| 113 | + <div align="left"> |
| 114 | + <ul style="list-style:none;"> |
| 115 | + <li> |
| 116 | + 1. 전역 변수 x와 y, 함수 out은 전역 실행 컨텍스트에 등록(push)되고, 런타임이 시작되면 전역 변수에 값이 할당되고 out이 호출됩니다. |
| 117 | + </li> |
| 118 | + <br> |
| 119 | + <li> |
| 120 | + 2. 함수 out이 호출되어 코드 제어권(Control)이 out 함수 내부로 이동한 뒤 out 함수 내부의 함수 코드를 평가하여 out 함수 실행 컨텍스트를 생성하고 스택에 추가(push)합니다. 이 때 지역 변수 x와 내부 함수 in이 out 함수 실행 컨텍스트에 등록되고, 런타임이므로 지역 변수에 값이 할당되고 내부 함수 in이 호출됩니다. |
| 121 | + </li> |
| 122 | + <br> |
| 123 | + <li> |
| 124 | + 3. 함수 in이 호출되어 코드 제어권이 in 함수 내부로 이동한 뒤, in 함수 내부의 함수 코드를 평가하여 in 함수 실행 컨텍스트를 생성하고 스택에 추가(push)합니다. 이 때 지역 변수 y가 in 함수 실행 컨텍스트에 등록되고, 런타임이므로 y에 값을 할당한 뒤 console.log 메서드를 호출하고 종료됩니다. |
| 125 | + </li> |
| 126 | + <br> |
| 127 | + <li> |
| 128 | + 1. in 함수가 종료되었으므로 코드 제어권이 다시 out 함수로 이동합니다. in 함수가 종료되었으므로 스택에서 제거(pop)됩니다. 그리고 in 함수는 더 이상 실행할 코드가 없으므로 종료됩니다. |
| 129 | + </li> |
| 130 | + <br> |
| 131 | + <li> |
| 132 | + 1. foo 함수가 종료되었으므로 코드 제어권은 다시 전역 코드로 이동합니다. out 함수가 종료되었으므로 스택에서 제거(pop)됩니다. 그리고 전역 코드는 더 이상 실행할 코드가 없으므로 종료되며 실행 컨텍스트에서 제거(pop)됩니다. |
| 133 | + </li> |
| 134 | + </ul> |
| 135 | + </div> |
| 136 | + |
| 137 | +</div> |
| 138 | + |
| 139 | +<br> |
| 140 | + |
| 141 | +이처럼 실행 컨텍스트 스택은 코드의 실행 순서를 관리하죠. 소스코드가 평가되면 실행 컨텍스트가 생성되고 실행 컨텍스트 스택의 최상위에 쌓입니다. 즉, **실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트**입니다. 이를 실행 중인 `실행 컨텍스트(Running Execution Context)`라고 합니다. |
| 142 | + |
| 143 | +<br> |
| 144 | + |
| 145 | +## 렉시컬 환경(Lexical Enviroment) |
| 146 | +> 식별자와 식별자에 바인딩 된 값, 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트입니다. |
| 147 | +
|
| 148 | +`실행 컨텍스트 스택`이 **코드의 실행 순서를 관리**한다면 `렉시컬 환경`은 **스코프와 식별자를 관리**합니다. |
0 commit comments