Skip to content

Commit

Permalink
docs: [23장] 실행 컨텍스트
Browse files Browse the repository at this point in the history
[23장] 실행 컨텍스트
  • Loading branch information
Gun woo authored Jul 28, 2023
2 parents 8bcad1f + e340481 commit 24c160b
Showing 1 changed file with 208 additions and 0 deletions.
208 changes: 208 additions & 0 deletions [23장] 실행 컨텍스트/건우.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# 실행 컨텍스트를 알아야 하는 이유
1. JS가 식별자와 식별자에 바인딩 된 값을 관리하는 방식을 알기 위함
2. **호이스팅**이 왜 발생하는지 알기 위해서
3. **클로저**의 동작 방식을 알기 위해서
4. **이벤트 핸들러****비동기 처리**의 동작 방식을 알기 위해서

# 소스코드 평가와 실행
+ JS에서는 소스코드를 평가과정과 실행과정으로 나누어서 실행합니다.
+ 평가 과정에서는 실행 컨텍스트를 생성하고 선언문만 먼저 실행하여 변수, 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록합니다.
+ 평가 과정이 끝나면 선언문을 제외한 소스코드를 순차적으로 실행합니다.
+ 실행 과정에서 변수 값의 변경 등, 실행 결과는 다시 실행 컨텍스트에 반영됩니다.

# 어떨때 실행 컨텍스트를 생성하는지?
1. 전역공간은 자동으로 컨텍스트로 구성된다.
2. 함수를 실행한다.
3. eval()함수를 실행한다.
4. block을 만든다 (ES6+)
5. 모듈 코드는 모듈 별로 독립적인 컨텍스트를 생성.

# 실행 컨텍스트의 역할
```js
const x = 1;
const y = 2;

function foo(a){
const x = 10;
const y = 20;

console.log(a + x + y);
}

foo(100);

console.log(x + y);
```
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/248b0210-e1d9-4ffe-be9f-76dd14e27f3d)<br>
이런식으로 코드가 실행되려면 스코프, 식별자, 코드 실행 순서등의 관리가 필요합니다. 이러한 관리를 하는 객체가 바로 실행 컨텍스트 입니다.

# 실행 컨텍스트 정의
+ 소스코드를 실행하는데 필요한 환경을 제공하고 실행 결과를 관리하는 영역입니다.
+ 스코프와 실행 순서 관리를 구현한 내부 메커니즘입니다.
+ 식별자와 스코프는 렉시컬 환경으로, 코드 실행 순서는 실행 컨텍스트 스택으로 관리합니다.

# 실행 컨텍스트 스택
```js
const x = 1;

function foo(){
//...
function bar(){
//...
}

bar();

}

foo();

함수 실행 순서는 전역 -> foo -> bar -> foo -> 전역
```
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/9b631714-150c-419a-914e-b628564e6342)<br>
+ 실행 컨텍스트 스택은 코드 실행 순서를 관리합니다.
+ 스택의 최상위에 있는 실행 컨텍스트는 언제나 현재 실행 중인 실행 컨텍스트입니다.
+ 평가과정이 끝나고 실행 컨텍스트가 생성되면 스택에 푸쉬합니다.
+ 실행과정이 끝나면 스택에서 해당 실행 컨텍스트를 팝 합니다.

# 렉시컬 환경
+ 책의 방식과 같이 LexicalEnvironment 와 VariableEnvironment 를 구분하지 않고 렉시컬 환경으로 통일해서 설명하겠습니다.
+ 렉시컬 환경은 식별자, 식별자에 바인딩 된 값, 상위 스코프 참조를 기록하는 자료구조 입니다. 실행 컨텍스트를 구성하는 일원입니다.
+ 렉시컬 환경은 환경 레코드, 외부 렉시컬 환경에 대한 참조로 구성됩니다.
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/66079b3e-f28b-4395-bc1f-d5742188634b)<br>
+ 환경 레코드는 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩 된 값을 관리하는 저장소입니다.
+ 외부 렉시컬 환경에 대한 참조는 상위 스코프를 가르킵니다. 스코프 체인을 구현하는 메커니즘입니다.

# 전역 실행 컨텍스트
```js
var x = 1;
const y = 2;

function foo (a){
var x = 3;
const y = 4;

function bar (b){
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
```
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/946b673a-4f87-4b7e-a74e-d0e283522ebf)<br>

### 호이스팅
+ 전역 코드 평가 과정에서 var 키워드로 선언한 전역 변수와 함수 선언문은 BindingObject를 통해 전역 객체의 프로퍼티, 메서드가 됩니다.
+ var 변수로 선언한 변수는 전역 객체의 프로퍼티가 되어 암묵적으로 undefined가 바인딩 됩니다. (변수 호이스팅)
+ 함수 선언문은 그 함수 이름과 동일한 식별자를 BindingObject를 통해 전역 객체의 키로 등록하고 생성된 함수 객체를 즉시 할당합니다. (함수 호이스팅)

```js
let foo = 1;

{
console.log(foo); //Error
let foo = 2;
}
```
+ 선언적 환경 레코드는 let, const 키워드로 선언된 식별자를 관리합니다.
+ let, const 키워드로 선언한 식별자는 선언단계와 초기화 단계가 분리되어 선언문에 도달하기 전까지 일시적 사각지대에 빠지게 되어 참조 불가능한 상태가 됩니다.
+ 초기화 단계는 런타임때 선언문에 도달했을때 시작됩니다.
+ let, const 키워드도 호이스팅이 일어나지만, 선언전에 참조하면 에러가 발생하도록 설정되었다고 보면 됩니다.

### 식별자 검색
+ 변수 할당문, 함수 호출문을 실행하기 위해선 해당 변수, 함수이름이 선언된 식별자인지 확인해야 합니다.
+ 만약 현재 렉시컬 환경에서 검색이 불가능하면 외부 렉시컬 참조를 타고 올라갑니다, 즉 상위 스코프를 타고 올라가서 식별자를 검색합니다.
+ 상위에서도 식별자를 찾지 못했다면 참조 에러를 발생시킵니다.

# 함수 실행 컨텍스트
```js
var x = 1;
const y = 2;

function foo (a){
var x = 3;
const y = 4;

function bar (b){
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
```
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/fd4754dd-6574-4b7d-bf28-3f931f33d03d)<br>

### 하나의 환경 레코드
+ 전역 환경 레코드는 객체 환경 레코드, 선언적 환경 레코드로 분리되었었습니다.
+ 하지만 함수 환경 레코드는 별도로 분리되지 않습니다. 따라서, var, let, const 모두 하나의 환경 레코드에 집어넣습니다.

### this 바인딩
+ 내부슬롯 [[ThisValue]]에 바인딩될 this는 함수 호출 방식에 따라 결정됩니다.
+ foo는 일반함수로 호출되었으므로 this는 전역 객체를 가르킵니다.
+ 만약 new와 함께 생성자 함수로 호출되었다면 해당 인스턴스를 가르키게 되겠지요.

### 상위 스코프 결정
+ 함수의 상위 스코프는 함수를 어디서 호출했느냐에 따라 결정하지 않고 어디서 정의했느냐에 따라 상위 스코프를 결정합니다.
+ 함수 정의를 평가할때 현재 실행중인 실행 컨텍스트의 렉시컬 환경을 함수 객체 [[Environment]]에 저장합니다.
+ 추후 함수가 실행될때 외부 렉시컬 환경에 대한 참조에 [[Environment]]에 저장된 객체가 할당되는 것이지요.
+ 나중에 클로저를 이해할때 중요한 개념이라고 합니다.
```js
var x = 1;
const y = 2;

function foo (a){
var x = 3;
const y = 4;

function bar (b){
const z = 5;
console.log(a + b + x + y + z);
}
bar(10);
}
foo(20);
```
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/1ef138f2-4597-4f66-a3f4-e36d4405e126)<br>

### 가비지 컬렉터
+ bar 함수 실행 컨텍스트가 스택에서 제거 되었을때, bar 함수 렉시컬 환경 까지 즉시 소멸하진 않습니다.
+ bar 함수의 렉시컬 환경을 누군가가 참조하고 있다면 가비지 컬렉터의 대상이 되지 않습니다.
+ 가비지 컬렉터는 누구에게도 참조되고 있지 않은 메모리 공간을 지웁니다.
+ 클로저의 단서가 될 것 같습니다.

# 블록 레벨 스코프
```js
let x = 1;
if (true){
let x = 10;
console.log(x); // 10
}
console.log(x); // 1
```
![image](https://github.com/prgrms-web-devcourse/FEDC4-deep-dive-study/assets/55936770/5ab9df5a-d26c-40cb-b468-6e7d75b0ac5c)
<br>
+ let, const 키워드로 선언된 변수는 블록 레벨 스코프를 따릅니다.
+ 이에반해 var 키워드로 선언된 변수는 함수 레벨 스코프를 따릅니다.
+ 블록을 만나게 되면 선언적 환경 레코드를 갖는 렉시컬 환경을 새롭게 생성하고 현재 실행중인 컨텍스트의 렉시컬환경을 교체합니다.

```js
console.log(a); // undefined

{
var a = 10;
let b = 20;
}

console.log(a); // 10
console.log(b); // 참조 에러
```
+ 블록안에 있는 var 키워드의 변수는 블록 렉시컬 환경에 포함되지 않음을 알 수 있습니다.

# 정리
1. 실행 컨텍스트는 실행하는데에 필요한 환경을 제공하고 코드의 실행 결과를 관리한다.
2. 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘이다.
3. 식별자, 스코프는 렉시컬 환경으로 관리한다.
4. 실행 순서는 실행 컨텍스트 스택으로 관리한다.
5. 렉시컬 환경은 환경 레코드와 외부 렉시컬 환경에 대한 참조로 구성되며 환경 레코드는 식별자를, 외부 렉시컬 환경 참조는 상위 스코프를 관리한다.

0 comments on commit 24c160b

Please sign in to comment.