Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

24-2장 클로저 - 고세종 #1010

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions docs/24_클로저/고세종.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
## 📕 오늘 공부한 내용 . ✒

**오늘 읽은 범위** : 24장

### 24. 클로저

클로저, 클로저의 정의를 알아보자.

**'클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.'**<br>
위 정의에서 이해해야할 핵심 키워드는 함수가 선언된 렉시컬 환경이다.

### 렉시컬 스코프

**자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프라 한다.**<br>
여기서 스코프는 실행 컨텍스트의 렉시컬 환경이다. 이 렉시컬 환경은 자신의 외부 렉시컬 환경에 대한 참조를 통해 상위 렉시컬 환경과 연결된다.<br>
이것이 바로 스코프 체인이다.

이 개념을 참조하면 **상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경에 의해 결정된다.**<br>
이것이 렉시컬 스코프이다.

### [[Environment]]

이전에 상위 스코프는 함수가 호출되는 시점이 아닌 정의되는 위치에 따라 결정된다고 했다.<br>
그렇다면, 어떤 정보를 가지고 상위 스코프를 참조할까 ?

바로 `[[Environment]]` 이다.

**함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.**<br>
자신의 내부 슬롯 [[Environment]]에 저장된 상위 스코프의 참조는 현재 실행 중인 실행 컨텍스트의 렉시컬 환경을 가르킨다.

이말이 당연한 이유는 상위 스코프는 호출되는 위치가 아닌 정의되는 위치이기 때문에, 함수가 정의되기 위해선 외부 환경의 실행 컨텍스트가 실행되고 있어야 되기 때문이다.

따라서 함수 객체의 내부슬롯 [[Environment]]에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 바로 상위 스코프이며,<br>
자신이 호출되었을 때 생성될 함수 렉시컬 환경의 '외부 렉시컬 환경에 대한 참조'에 저장될 참조 값이다.

반대로 말하면 **외부 렉시컬 환경에 대한 참조에는 함수 객체의 내부 슬롯 [[Environment]]에 저장된 렉시컬 환경의 참조가 할당된다.**

### 클로저와 렉시컬 환경

**외부함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 여기서 이 중첩 함수를 클로저라고 한다.**

```javascript
const x = 1;

function outer() {
const x = 10;
const inner = function () {
console.log(x);
};
return inner;
}

const innerFunc = outer();
innerFunc();
```

위 예제의 실행 과정을 확인해 본다면, `outer`의 실행 컨텍스트가 실행되며 렉시컬 환경이 생성되고 내부 함수 `inner` 함수의 렉시컬 환경이 생성된다.<br>
그후 `innerFunc()`가 실행 되면서 outer의 실행 컨텍스트가 종료되는데, 이와 같이 outer의 렉시컬 환경이 소멸될거라고 생각할 수 있다.

하지만, inner함수가 outer의 변수 x값을 참조하고 있는 이상 outer의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거 되지만 렉시컬 환경은 소멸되지 않는다.
inner의 내부 슬롯 [[Environment]]에는 outer의 렉시컬 환경이 저장된다.

하지만 내부 중첩 함수이면서 클로저 함수가 아닌 경우도 있다.

첫번째로 내부 중첩 함수가 외부 함수의 어떤 변수도 참조하지 않는 경우이다.
두번째는 외부함수가 종료되면서 내부 함수도 소멸 되는 경우이다.

**클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.**

클로저에는 **자유 변수**라는 개념이 있다.

한가지 예를 확인해보자.

```javascript
function foo() {
const x = 1;
const y = 2;

function bar() {
console.log(x);
}
return bar;
}

const bar = foo();
```

함수 `bar`는 `foo`함수의 내부 함수로서 foo의 식별자 x를 참조하고 있으며, 생명주기도 길기 때문에 클로저라고 할 수 있다.<br>
위 예제에서 한가지 알아둬야 할 점은 bar 함수가 y는 참조하지 않는다는 점이다.

이럴 경우 대부분의 브라우저는 최적화를 통해 상위 스코프의 식별자 중 클로저가 참조하고 있는 식별자인 x만 기억한다.<br>
여기서 참조되는 상위 스코프의 변수 x를 **자유 변수**라고 한다.
Loading