Skip to content

Commit 12a0556

Browse files
[변수의 유효범위와 클로저] 렉시컬 환경 부분 업데이트
1 parent d009c74 commit 12a0556

File tree

1 file changed

+13
-13
lines changed

1 file changed

+13
-13
lines changed

1-js/06-advanced-functions/03-closure/article.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ alert( counter() ); // 2
175175

176176
이렇게 스크립트 전체와 관련된 렉시컬 환경은 전역 렉시컬 환경(global Lexical Environment)이라고 합니다.
177177

178-
위 그림에서 네모 상자는 변수가 저장되는 환경 레코드를 나타내고 붉은 화살표는 외부 참조(outer reference)를 나타냅니다. 전역 렉시컬 환경은 외부 참조를 갖지 않기 때문에 화살표가 `null`을 가리키는 걸 확인할 수 있습니다.
178+
위 그림에서 네모 상자는 변수가 저장되는 환경 레코드를 나타내고 붉은 화살표는 외부 렉시컬 환경에 대한 참조를 나타냅니다. 전역 렉시컬 환경은 외부 참조를 갖지 않기 때문에 화살표가 `null`을 가리키는 걸 확인할 수 있습니다.
179179

180180
코드가 실행되고 실행 흐름이 이어져 나가면서 렉시컬 환경은 변화합니다.
181181

@@ -186,7 +186,7 @@ alert( counter() ); // 2
186186
우측의 네모 상자들은 코드가 한 줄, 한 줄 실행될 때마다 전역 렉시컬 환경이 어떻게 변화하는지 보여줍니다.
187187

188188
1. 스크립트가 시작되면 스크립트 내에서 선언한 변수 전체가 렉시컬 환경에 올라갑니다(pre-populated).
189-
- 이때 변수의 상태는 특수 내부 상태(special internal state)인 'uninitialized'가 됩니다. 자바스크립트 엔진은 'uninitialized' 상태의 변수를 인지하긴 하지만, `let`을 만나기 전까진 이 변수를 참조할 수 없습니다.
189+
- 이때 변수의 상태는 특수 내부 상태(special internal state)인 'uninitialized'가 됩니다. 자바스크립트 엔진은 uninitialized 상태의 변수를 인지하긴 하지만, `let`을 만나기 전까진 이 변수를 참조할 수 없습니다.
190190
2. `let phrase`가 나타났네요. 아직 값을 할당하기 전이기 때문에 프로퍼티 값은 `undefined`입니다. `phrase`는 이 시점 이후부터 사용할 수 있습니다.
191191
3. `phrase`에 값이 할당되었습니다.
192192
4. `phrase`의 값이 변경되었습니다.
@@ -199,7 +199,7 @@ alert( counter() ); // 2
199199
```smart header="렉시컬 환경은 명세서에만 존재합니다."
200200
'렉시컬 환경'은 [명세서](https://tc39.es/ecma262/#sec-lexical-environments)에서 자바스크립트가 어떻게 동작하는지 설명하는 데 쓰이는 '이론상의' 객체입니다. 따라서 코드를 사용해 직접 렉시컬 환경을 얻거나 조작하는 것은 불가능합니다.
201201
202-
자바스크립트 엔진들은 명세서에 언급된 사항을 준수하면서 엔진 고유의 방법을 사용해 렉시컬 환경을 최적화합니다. 사용하지 않는 변수를 버려 메모리를 절약하거나 기타 내부 트릭을 써서 말이죠.
202+
자바스크립트 엔진들은 명세서에 언급된 사항을 준수하면서 엔진 고유의 방법을 사용해 렉시컬 환경을 최적화합니다. 사용하지 않는 변수를 버려 메모리를 절약하거나 다양한 내부 트릭을 써서 말이죠.
203203
```
204204

205205
### 단계 2. 함수 선언문
@@ -216,7 +216,7 @@ alert( counter() ); // 2
216216

217217
![](closure-function-declaration.svg)
218218

219-
이런 동작 방식은 함수 선언문으로 정의한 함수에만 적용됩니다. `let say = function(name)...`같이 함수를 변수에 할당한 함수 표현식(Function Expression)은 해당하지 않습니다.
219+
이런 동작 방식은 함수 선언문으로 정의한 함수에만 적용됩니다. `let say = function(name)...`같이 함수를 변수에 할당한 함수 표현식(function expression)은 해당하지 않습니다.
220220

221221
### 단계 3. 내부와 외부 렉시컬 환경
222222

@@ -237,7 +237,7 @@ alert( counter() ); // 2
237237

238238
![](lexical-environment-simple.svg)
239239

240-
함수가 호출 중인 동안은 호출 중인 함수를 위한 내부 렉시컬 환경과 내부 렉시컬 환경이 가리키는 외부(전역) 렉시컬 환경 두 개를 갖게 됩니다.
240+
함수가 호출 중인 동안엔 호출 중인 함수를 위한 내부 렉시컬 환경과 내부 렉시컬 환경이 가리키는 외부 렉시컬 환경을 갖게 .
241241

242242
- 예시의 내부 렉시컬 환경은 현재 실행 중인 함수인 `say`에 상응합니다. 내부 렉시컬 환경엔 함수의 인자인 `name`으로부터 유래한 프로퍼티 하나만 있네요. `say("John")`을 호출했기 때문에, `name`의 값은 `"John"`이 됩니다.
243243
- 예시의 외부 렉시컬 환경은 전역 렉시컬 환경입니다. 전역 렉시컬 환경은 `phrase`와 함수 `say`를 프로퍼티로 갖습니다.
@@ -256,7 +256,7 @@ alert( counter() ); // 2
256256
![lexical environment lookup](lexical-environment-simple-lookup.svg)
257257

258258

259-
### 단계 4. 반환 함수
259+
### 단계 4. 함수를 반환하는 함수
260260

261261
`makeCounter` 예시로 돌아가 봅시다.
262262

@@ -272,29 +272,29 @@ function makeCounter() {
272272
let counter = makeCounter();
273273
```
274274

275-
`makeCounter()`를 호출하면 호출할 때마다 새로운 렉시컬 환경 객체가 만들어집니다. 그리고 이 렉시컬 환경 개체엔 `makeCounter`를 실행하는데 필요한 변수들이 저장됩니다.
275+
`makeCounter()`를 호출하면 호출할 때마다 새로운 렉시컬 환경 객체가 만들어지고 여기에 `makeCounter`를 실행하는데 필요한 변수들이 저장됩니다.
276276

277277
위쪽에서 살펴본 `say("John")` 예시와 마찬가지로 `makeCounter()`를 호출할 때도 두 개의 렉시컬 환경이 만들어집니다.
278278

279279
![](closure-makecounter.svg)
280280

281-
그런데 `say("John")``makeCounter()` 예시에는 차이점이 하나 있습니다. `makeCounter()`가 실행되는 도중에 한 줄짜리 본문(`return count++`)을 가진 중첩 함수가 만들어진다는 점입니다. 현재는 중첩함수가 생성되기만 하고 실행은 되지 않은 상태입니다.
281+
그런데 위쪽에서 살펴본 `say("John")` 예시와 `makeCounter()` 예시에는 차이점이 하나 있습니다. `makeCounter()`가 실행되는 도중엔 본문(`return count++`)이 한줄 짜리인 중첩 함수가 만들어진다는 점입니다. 현재는 중첩함수가 생성되기만 하고 실행은 되지 않은 상태입니다.
282282

283283
여기서 중요한 사실이 하나 있습니다. 모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다는 점입니다. 함수는 `[[Environment]]`라 불리는 숨김 프로퍼티를 갖는데, 여기에 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장됩니다.
284284

285285
![](closure-makecounter-environment.svg)
286286

287-
따라서 `counter.[[Environment]]``{count: 0}`이 있는 렉시컬 환경에 대한 참조가 저장됩니다. 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 `[[Environment]]` 프로퍼티 덕분입니다. `[[Environment]]`는 함수가 생성될 때 딱 한 번 값이 세팅됩니다. 그리고 이 값은 영원히 변하지 않습니다.
287+
따라서 `counter.[[Environment]]``{count: 0}`이 있는 렉시컬 환경에 대한 참조가 저장됩니다. 호출 장소와 상관없이 함수가 자신이 태어난 곳을 기억할 수 있는 건 바로 `[[Environment]]` 프로퍼티 덕분입니다. `[[Environment]]`는 함수가 생성될 때 딱 한 번 값이 세팅되고 영원히 변하지 않습니다.
288288

289-
`counter()`를 호출하면 각 호출마다 새로운 렉시컬 환경이 만들어집니다. 그리고 이 렉시컬 환경은 `counter.[[Environment]]`에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조하게 됩니다.
289+
`counter()`를 호출하면 각 호출마다 새로운 렉시컬 환경이 생성됩니다. 그리고 이 렉시컬 환경은 `counter.[[Environment]]`에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조합니다.
290290

291291
![](closure-makecounter-nested-call.svg)
292292

293293
실행 흐름이 중첩 함수의 본문으로 넘어오면 `count` 변수가 필요한데, 먼저 자체 렉시컬 환경에서 변수를 찾습니다. 익명 중첩 함수엔 지역변수가 없기 때문에 이 렉시컬 환경은 비어있는 상황입니다(`<empty>`). 이제 `counter()`의 렉시컬 환경이 참조하는 외부 렉시컬 환경에서 `count`를 찾아봅시다. `count`를 찾았습니다!
294294

295-
**변숫값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄집니다.**
295+
이제 `count++`가 실행되면서 count 값이 1 증가해야하는데, **변숫값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄집니다.**
296296

297-
실행이 종료된 후의 상태는 다음과 같습니다.
297+
따라서 실행이 종료된 후의 상태는 다음과 같습니다.
298298

299299
![](closure-makecounter-nested-call-2.svg)
300300

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

0 commit comments

Comments
 (0)