You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/06-advanced-functions/03-closure/article.md
+13-13Lines changed: 13 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -175,7 +175,7 @@ alert( counter() ); // 2
175
175
176
176
이렇게 스크립트 전체와 관련된 렉시컬 환경은 전역 렉시컬 환경(global Lexical Environment)이라고 합니다.
177
177
178
-
위 그림에서 네모 상자는 변수가 저장되는 환경 레코드를 나타내고 붉은 화살표는 외부 참조(outer reference)를 나타냅니다. 전역 렉시컬 환경은 외부 참조를 갖지 않기 때문에 화살표가 `null`을 가리키는 걸 확인할 수 있습니다.
178
+
위 그림에서 네모 상자는 변수가 저장되는 환경 레코드를 나타내고 붉은 화살표는 외부 렉시컬 환경에 대한 참조를 나타냅니다. 전역 렉시컬 환경은 외부 참조를 갖지 않기 때문에 화살표가 `null`을 가리키는 걸 확인할 수 있습니다.
179
179
180
180
코드가 실행되고 실행 흐름이 이어져 나가면서 렉시컬 환경은 변화합니다.
181
181
@@ -186,7 +186,7 @@ alert( counter() ); // 2
186
186
우측의 네모 상자들은 코드가 한 줄, 한 줄 실행될 때마다 전역 렉시컬 환경이 어떻게 변화하는지 보여줍니다.
187
187
188
188
1. 스크립트가 시작되면 스크립트 내에서 선언한 변수 전체가 렉시컬 환경에 올라갑니다(pre-populated).
189
-
- 이때 변수의 상태는 특수 내부 상태(special internal state)인 'uninitialized'가 됩니다. 자바스크립트 엔진은 'uninitialized' 상태의 변수를 인지하긴 하지만, `let`을 만나기 전까진 이 변수를 참조할 수 없습니다.
189
+
- 이때 변수의 상태는 특수 내부 상태(special internal state)인 'uninitialized'가 됩니다. 자바스크립트 엔진은 uninitialized 상태의 변수를 인지하긴 하지만, `let`을 만나기 전까진 이 변수를 참조할 수 없습니다.
190
190
2.`let phrase`가 나타났네요. 아직 값을 할당하기 전이기 때문에 프로퍼티 값은 `undefined`입니다. `phrase`는 이 시점 이후부터 사용할 수 있습니다.
191
191
3.`phrase`에 값이 할당되었습니다.
192
192
4.`phrase`의 값이 변경되었습니다.
@@ -199,7 +199,7 @@ alert( counter() ); // 2
199
199
```smart header="렉시컬 환경은 명세서에만 존재합니다."
200
200
'렉시컬 환경'은 [명세서](https://tc39.es/ecma262/#sec-lexical-environments)에서 자바스크립트가 어떻게 동작하는지 설명하는 데 쓰이는 '이론상의' 객체입니다. 따라서 코드를 사용해 직접 렉시컬 환경을 얻거나 조작하는 것은 불가능합니다.
201
201
202
-
자바스크립트 엔진들은 명세서에 언급된 사항을 준수하면서 엔진 고유의 방법을 사용해 렉시컬 환경을 최적화합니다. 사용하지 않는 변수를 버려 메모리를 절약하거나 기타 내부 트릭을 써서 말이죠.
202
+
자바스크립트 엔진들은 명세서에 언급된 사항을 준수하면서 엔진 고유의 방법을 사용해 렉시컬 환경을 최적화합니다. 사용하지 않는 변수를 버려 메모리를 절약하거나 다양한 내부 트릭을 써서 말이죠.
203
203
```
204
204
205
205
### 단계 2. 함수 선언문
@@ -216,7 +216,7 @@ alert( counter() ); // 2
216
216
217
217

218
218
219
-
이런 동작 방식은 함수 선언문으로 정의한 함수에만 적용됩니다. `let say = function(name)...`같이 함수를 변수에 할당한 함수 표현식(Function Expression)은 해당하지 않습니다.
219
+
이런 동작 방식은 함수 선언문으로 정의한 함수에만 적용됩니다. `let say = function(name)...`같이 함수를 변수에 할당한 함수 표현식(function expression)은 해당하지 않습니다.
220
220
221
221
### 단계 3. 내부와 외부 렉시컬 환경
222
222
@@ -237,7 +237,7 @@ alert( counter() ); // 2
237
237
238
238

239
239
240
-
함수가 호출 중인 동안은 호출 중인 함수를 위한 내부 렉시컬 환경과 내부 렉시컬 환경이 가리키는 외부(전역) 렉시컬 환경 두 개를 갖게 됩니다.
240
+
함수가 호출 중인 동안엔 호출 중인 함수를 위한 내부 렉시컬 환경과 내부 렉시컬 환경이 가리키는 외부 렉시컬 환경을 갖게 .
241
241
242
242
- 예시의 내부 렉시컬 환경은 현재 실행 중인 함수인 `say`에 상응합니다. 내부 렉시컬 환경엔 함수의 인자인 `name`으로부터 유래한 프로퍼티 하나만 있네요. `say("John")`을 호출했기 때문에, `name`의 값은 `"John"`이 됩니다.
243
243
- 예시의 외부 렉시컬 환경은 전역 렉시컬 환경입니다. 전역 렉시컬 환경은 `phrase`와 함수 `say`를 프로퍼티로 갖습니다.
`makeCounter()`를 호출하면 호출할 때마다 새로운 렉시컬 환경 객체가 만들어집니다. 그리고 이 렉시컬 환경 개체엔`makeCounter`를 실행하는데 필요한 변수들이 저장됩니다.
275
+
`makeCounter()`를 호출하면 호출할 때마다 새로운 렉시컬 환경 객체가 만들어지고 여기에`makeCounter`를 실행하는데 필요한 변수들이 저장됩니다.
276
276
277
277
위쪽에서 살펴본 `say("John")` 예시와 마찬가지로 `makeCounter()`를 호출할 때도 두 개의 렉시컬 환경이 만들어집니다.
278
278
279
279

280
280
281
-
그런데 `say("John")`와 `makeCounter()` 예시에는 차이점이 하나 있습니다. `makeCounter()`가 실행되는 도중에 한 줄짜리 본문(`return count++`)을 가진 중첩 함수가 만들어진다는 점입니다. 현재는 중첩함수가 생성되기만 하고 실행은 되지 않은 상태입니다.
281
+
그런데 위쪽에서 살펴본 `say("John")` 예시와 `makeCounter()` 예시에는 차이점이 하나 있습니다. `makeCounter()`가 실행되는 도중엔 본문(`return count++`)이 한줄 짜리인 중첩 함수가 만들어진다는 점입니다. 현재는 중첩함수가 생성되기만 하고 실행은 되지 않은 상태입니다.
282
282
283
283
여기서 중요한 사실이 하나 있습니다. 모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다는 점입니다. 함수는 `[[Environment]]`라 불리는 숨김 프로퍼티를 갖는데, 여기에 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장됩니다.
284
284
285
285

286
286
287
-
따라서 `counter.[[Environment]]`엔 `{count: 0}`이 있는 렉시컬 환경에 대한 참조가 저장됩니다. 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 `[[Environment]]` 프로퍼티 덕분입니다. `[[Environment]]`는 함수가 생성될 때 딱 한 번 그 값이 세팅됩니다. 그리고 이 값은 영원히 변하지 않습니다.
287
+
따라서 `counter.[[Environment]]`엔 `{count: 0}`이 있는 렉시컬 환경에 대한 참조가 저장됩니다. 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 이 `[[Environment]]` 프로퍼티 덕분입니다. `[[Environment]]`는 함수가 생성될 때 딱 한 번 값이 세팅되고 영원히 변하지 않습니다.
288
288
289
-
`counter()`를 호출하면 각 호출마다 새로운 렉시컬 환경이 만들어집니다. 그리고 이 렉시컬 환경은 `counter.[[Environment]]`에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조하게 됩니다.
289
+
`counter()`를 호출하면 각 호출마다 새로운 렉시컬 환경이 생성됩니다. 그리고 이 렉시컬 환경은 `counter.[[Environment]]`에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조합니다.
290
290
291
291

292
292
293
293
실행 흐름이 중첩 함수의 본문으로 넘어오면 `count` 변수가 필요한데, 먼저 자체 렉시컬 환경에서 변수를 찾습니다. 익명 중첩 함수엔 지역변수가 없기 때문에 이 렉시컬 환경은 비어있는 상황입니다(`<empty>`). 이제 `counter()`의 렉시컬 환경이 참조하는 외부 렉시컬 환경에서 `count`를 찾아봅시다. `count`를 찾았습니다!
294
294
295
-
**변숫값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄집니다.**
295
+
이제 `count++`가 실행되면서 count 값이 1 증가해야하는데, **변숫값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄집니다.**
296
296
297
-
실행이 종료된 후의 상태는 다음과 같습니다.
297
+
따라서 실행이 종료된 후의 상태는 다음과 같습니다.
298
298
299
299

300
300
@@ -305,7 +305,7 @@ let counter = makeCounter();
305
305
306
306
[클로저](https://en.wikipedia.org/wiki/Closure_(computer_programming))는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미합니다. 몇몇 언어에선 클로저를 구현하는 게 불가능하거나 특수한 방식으로 함수를 작성해야 클로저를 만들 수 있습니다. 하지만 자바스크립트에선 모든 함수가 자연스럽게 클로저가 됩니다. 예외가 하나 있긴 한데 자세한 내용은 <info:new-function>에서 다루도록 하겠습니다.
307
307
308
-
요점을 정리해 봅시다. 자바스크립트의 함수는 숨김 프로퍼티인 `[[Environment]]`를 이용해 자신이 어디서 만들어졌는지를 기억합니다. 함수 내부의 코드는 `[[Environment]]`를 사용해 외부 변수에 접근합니다.
308
+
요점을 정리해 봅시다. 자바스크립트의 함수는 숨김 프로퍼티인 `[[Environment]]`를 이용해 자신이 어디서 만들어졌는지를 기억합니다. 함수 본문에선 `[[Environment]]`를 사용해 외부 변수에 접근합니다.
309
309
310
310
프런트엔드 개발자 채용 인터뷰에서 "클로저가 무엇입니까?"라는 질문을 받으면, 클로저의 정의를 말하고 자바스크립트에서 왜 모든 함수가 클로저인지에 관해 설명하면 될 것 같습니다. 이때 `[[Environment]]` 프로퍼티와 렉시컬 환경이 어떤 방식으로 동작하는지에 대한 설명을 덧붙이면 좋습니다.
0 commit comments