aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhochan222 <hochan049@gmail.com>2021-07-28 22:05:57 +0900
committerKyle <mkitigy@gmail.com>2021-08-14 13:40:00 +0900
commit39071f00869a0e3c88b821101b2890044c3b4404 (patch)
treece2a5f9a9c34ba728e74a264ea5a8367a543fe83
parentc19de49ba6ddaa939569f157db8d8c8cc7036d23 (diff)
downloadtranslated-content-39071f00869a0e3c88b821101b2890044c3b4404.tar.gz
translated-content-39071f00869a0e3c88b821101b2890044c3b4404.tar.bz2
translated-content-39071f00869a0e3c88b821101b2890044c3b4404.zip
feat: convert html to markdown, Closures
-rw-r--r--files/ko/web/javascript/closures/index.md734
1 files changed, 392 insertions, 342 deletions
diff --git a/files/ko/web/javascript/closures/index.md b/files/ko/web/javascript/closures/index.md
index dc6fdbc41c..f3e6cfaac8 100644
--- a/files/ko/web/javascript/closures/index.md
+++ b/files/ko/web/javascript/closures/index.md
@@ -10,360 +10,407 @@ tags:
translation_of: Web/JavaScript/Closures
original_slug: Web/JavaScript/Guide/Closures
---
-<div>{{jsSidebar("Intermediate")}}</div>
+{{jsSidebar("Intermediate")}}
-<p>클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.</p>
+**클로저**는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.
-<h2 id="어휘적_범위_지정Lexical_scoping">어휘적 범위 지정(Lexical scoping)</h2>
+## 어휘적 범위 지정(Lexical scoping)
-<p>다음을 보자:</p>
+다음을 보자:
-<pre>function init() {
- var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
- function displayName() { // displayName() 은 내부 함수이며, 클로저다.
- alert(name); // 부모 함수에서 선언된 변수를 사용한다.
- }
- displayName();
-}
-init();</pre>
+```js
+ function init() {
+ var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
+ function displayName() { // displayName() 은 내부 함수이며, 클로저다.
+ alert(name); // 부모 함수에서 선언된 변수를 사용한다.
+ }
+ displayName();
+ }
+ init();
+```
-<p><code>init()</code>은 지역 변수 <code>name</code>과 함수 <code>displayName()</code>을 생성한다. <code>displayName()</code>은 <code>init()</code> 안에 정의된 내부 함수이며 <code>init()</code> 함수 본문에서만 사용할 수 있다. 여기서 주의할 점은 <code>displayName()</code> 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 <code>displayName()</code> 역시 부모 함수 <code>init()</code>에서 선언된 변수 <code>name</code>에 접근할 수 있다. 만약 <code>displayName()</code>가 자신만의 <code>name</code>변수를 가지고 있었다면, <code>name</code>대신 <code>this.name</code>을 사용했을 것이다.</p>
+`init()`은 지역 변수 `name`과 함수 `displayName()`을 생성한다. `displayName()`은
+`init()` 안에 정의된 내부 함수이며 `init()` 함수 본문에서만 사용할 수 있다. 여기서 주의할 점은 `displayName()` 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 `displayName()` 역시 부모 함수 `init()`에서 선언된 변수 `name`에 접근할 수 있다. 만약 `displayName()`가 자신만의 `name`변수를 가지고 있었다면, `name`대신 `this.name`을 사용했을 것이다.
-<p>{{JSFiddleEmbed("https://jsfiddle.net/78dg25ax/", "js,result", 250)}}</p>
+{{JSFiddleEmbed("https://jsfiddle.net/78dg25ax/", "js,result", 250)}}
-<p>위 코드를 <a href="http://jsfiddle.net/xAFs9/3/">실행</a>하면 <code>displayName()</code> 함수 내의 <code>alert()</code>문이 부모 함수에서 정의한 변수 <code>name</code>의 값을 성공적으로 출력한다. 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다. 이는 어휘적 범위 지정(lexical scoping)의 한 예이다. 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.</p>
+위 코드를 [실행](http://jsfiddle.net/xAFs9/3/)하면 `displayName()` 함수 내의
+`alert()`문이 부모 함수에서 정의한 변수 `name`의 값을 성공적으로 출력한다. 이 예시를 통해 함수가 중첩된 상황에서 파서가 어떻게 변수를 처리하는지 알 수 있다. 이는 어휘적 범위 지정(lexical scoping)의 한 예이다. 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.
-<h2 id="클로저Closure">클로저(Closure)</h2>
+## 클로저(Closure)
-<p>이제 다음 예제를 보자:</p>
+이제 다음 예제를 보자:
-<pre>function makeFunc() {
- var name = "Mozilla";
- function displayName() {
- alert(name);
- }
- return displayName;
-}
+```js
+ function makeFunc() {
+ var name = "Mozilla";
+ function displayName() {
+ alert(name);
+ }
+ return displayName;
+ }
-var myFunc = makeFunc();
-//myFunc변수에 displayName을 리턴함
-//유효범위의 어휘적 환경을 유지
-myFunc();
-//리턴된 displayName 함수를 실행(name 변수에 접근)</pre>
+ var myFunc = makeFunc();
+ //myFunc변수에 displayName을 리턴함
+ //유효범위의 어휘적 환경을 유지
+ myFunc();
+ //리턴된 displayName 함수를 실행(name 변수에 접근)
+```
-<p>이 코드는 바로 전의 예제와 완전히 동일한 결과가 실행된다. 하지만 흥미로운 차이는 <code>displayName()</code>함수가 실행되기 전에 외부함수인 <code>makeFunc()</code>로부터 리턴되어 <code>myFunc</code> 변수에 저장된다는 것이다.</p>
+이 코드는 바로 전의 예제와 완전히 동일한 결과가 실행된다. 하지만 흥미로운 차이는 `displayName()`함수가 실행되기 전에 외부함수인 `makeFunc()`로부터 리턴되어 `myFunc` 변수에 저장된다는 것이다.
-<p>한 눈에 봐서는 이 코드가 여전히 작동하는 것이 직관적으로 보이지 않을 수 있다. 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재한다. <code>makeFunc()</code> 실행이 끝나면(<code>displayName</code>함수가 리턴되고 나면) <code>name</code> 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적이다.</p>
+한 눈에 봐서는 이 코드가 여전히 작동하는 것이 직관적으로 보이지 않을 수 있다. 몇몇 프로그래밍 언어에서, 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재한다. `makeFunc()` 실행이 끝나면(`displayName`함수가 리턴되고 나면) `name` 변수에 더 이상 접근할 수 없게 될 것으로 예상하는 것이 일반적이다.
-<p>하지만 위의 예시와 자바스크립트의 경우는 다르다. 그 이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다. 첫 번째 예시의 경우, <code>myFunc</code>은 <code>makeFunc</code>이 실행될 때 생성된 <code>displayName</code> 함수의 인스턴스에 대한 참조다. <code>displayName</code>의 인스턴스는 변수 <code>name</code> 이 있는 어휘적 환경에 대한 참조를 유지한다. 이런 이유로 <code>myFunc</code>가 호출될 때 변수 <code>name</code>은 사용할 수 있는 상태로 남게 되고 "Mozilla" 가 <code>alert</code> 에 전달된다.</p>
+하지만 위의 예시와 자바스크립트의 경우는 다르다. 그 이유는 자바스크립트는 함수를 리턴하고, 리턴하는 함수가 클로저를 형성하기 때문이다. 클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다. 첫 번째 예시의 경우, `myFunc`은 `makeFunc`이 실행
+될 때 생성된 `displayName` 함수의 인스턴스에 대한 참조다. `displayName`의 인스턴스는 변수 `name` 이 있는 어휘적 환경에 대한 참조를 유지한다. 이런 이유로 `myFunc`가 호출될 때 변수 `name`은 사용할 수 있는 상태로 남게 되고 "Mozilla" 가 `alert` 에 전달된다.
-<p>다음은 조금 더 흥미로운 예제인 makeAdder 함수이다:</p>
+다음은 조금 더 흥미로운 예제인 makeAdder 함수이다:
-<pre>function makeAdder(x) {
- var y = 1;
- return function(z) {
- y = 100;
- return x + y + z;
- };
-}
+```js
+ function makeAdder(x) {
+ var y = 1;
+ return function(z) {
+ y = 100;
+ return x + y + z;
+ };
+ }
-var add5 = makeAdder(5);
-var add10 = makeAdder(10);
-//클로저에 x와 y의 환경이 저장됨
+ var add5 = makeAdder(5);
+ var add10 = makeAdder(10);
+ //클로저에 x와 y의 환경이 저장됨
-console.log(add5(2)); // 107 (x:5 + y:100 + z:2)
-console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
-//함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
+ console.log(add5(2)); // 107 (x:5 + y:100 + z:2)
+ console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
+ //함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산
+```
-</pre>
+이 예제에서 단일 인자 `x`를 받아서 새 함수를 반환하는 함수 `makeAdder(x)`를 정의했다. 반환되는 함수는 단일인자 z를 받아서 x와 y와 z의 합을 반환한다.
-<p>이 예제에서 단일 인자 <code>x</code>를 받아서 새 함수를 반환하는 함수 <code>makeAdder(x)를 정의했다.</code> 반환되는 함수는 단일 인자 <font face="consolas, Liberation Mono, courier, monospace">z</font>를 받아서 x와 y와 z의 합을 반환한다.</p>
+본질적으로 `makeAdder`는 함수를 만들어내는 공장이다. 이는 `makeAdder`함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 `add5, add10` 두 개의 새로운 함수들을 만들기 위해 `makeAdder`함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.
-<p>본질적으로 <code>makeAdder</code>는 함수를 만들어내는 공장이다. 이는 <code>makeAdder</code>함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 <code>add5, add10</code> 두 개의 새로운 함수들을 만들기 위해 <code>makeAdder</code>함수 공장을 사용했다. 하나는 매개변수 <font face="consolas, Liberation Mono, courier, monospace">x</font>에 5를 더하고 다른 하나는 매개변수 <font face="consolas, Liberation Mono, courier, monospace">x</font>에 10을 더한다.</p>
+`add5`와 `add10`은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 `add5`의 맥락적 환경에서 클로저 내부의 x는 5 이지만 `add10`의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.) 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는것이 아니라는 것을 의미한다.
-<p><code>add5</code>와 <code>add10</code>은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 <code>add5</code>의 맥락적 환경에서 클로저 내부의 x는 5 이지만 <code>add10</code>의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.) 이는 클로저가 리턴된 후에도 외부함수의 변수들에 접근 가능하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는 것이 아니라는 것을 의미한다.</p>
+## 실용적인 클로저
-<h2 id="실용적인_클로저">실용적인 클로저</h2>
+클로저는 어떤 데이터(어휘적 환경)와 그 데이터를 조작하는 함수를 연관시켜주기 때문에 유용하다. 이것은 객체가 어떤 데이터와(그 객체의 속성) 하나 혹은 그 이상의 메소드들을 연관시킨다는 점에서 객체지향 프로그래밍과 분명히 같은 맥락에 있다.
-<p>클로저는 어떤 데이터(어휘적 환경)와 그 데이터를 조작하는 함수를 연관시켜주기 때문에 유용하다. 이것은 객체가 어떤 데이터와(그 객체의 속성) 하나 혹은 그 이상의 메소드들을 연관시킨다는 점에서 객체지향 프로그래밍과 분명히 같은 맥락에 있다.</p>
+결론적으로 오직 하나의 메소드를 가지고 있는 객체를 일반적으로 사용하는 모든 곳에 클로저를 사용할 수 있다.
-<p>결론적으로 오직 하나의 메소드를 가지고 있는 객체를 일반적으로 사용하는 모든 곳에 클로저를 사용할 수 있다.</p>
+이렇게 할 수 있는 상황은 특히 웹에서 일반적이다. 프론트 엔드 자바스크립트에서 우리가 쓰는 많은 코드가 이벤트 기반이다. 우리는 몇 가지 동작을 정의한 다음 사용자에 의한 이벤트에(클릭 혹은 키 누르기 같은) 연결한다. 우리의 코드는 일반적으로 콜백으로 첨부된다: 이벤트에 응답하여 실행되는 단일 함수다.
-<p>이렇게 할 수 있는 상황은 특히 웹에서 일반적이다. 프론트 엔드 자바스크립트에서 우리가 쓰는 많은 코드가 이벤트 기반이다. 우리는 몇 가지 동작을 정의한 다음 사용자에 의한 이벤트에(클릭 혹은 키 누르기 같은) 연결한다. 우리의 코드는 일반적으로 콜백으로 첨부된다: 이벤트에 응답하여 실행되는 단일 함수다.</p>
+예를 들면 페이지의 글자 크기를 조정하는 몇 개의 버튼을 추가한다고 가정하자. 이 작업을 수행하는 한 가지 방법은 `body` 요소의 font-size를 픽셀 단위로 지정하고 상대적인 `em` 단위를 사용하여 페이지의 다른 요소들의 (예: 헤더) 크기를 설정하는 것이다.
-<p>예를 들면 페이지의 글자 크기를 조정하는 몇 개의 버튼을 추가한다고 가정하자. 이 작업을 수행하는 한 가지 방법은 body 요소의 font-size를 픽셀 단위로 지정하고 상대적인 em 단위를 사용하여 페이지의 다른 요소들의 (예: 헤더) 크기를 설정하는 것이다.</p>
+```css
+ body {
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ }
-<pre>body {
- font-family: Helvetica, Arial, sans-serif;
- font-size: 12px;
-}
+ h1 {
+ font-size: 1.5em;
+ }
-h1 {
- font-size: 1.5em;
-}
+ h2 {
+ font-size: 1.2em;
+ }
+```
-h2 {
- font-size: 1.2em;
-}
-</pre>
+우리의 대화식 글자 크기 버튼들은 `body` 요소의 `font-size` 속성을 변경할 수 있고 이런 조정은 상대적 단위들 덕분에 페이지의 다른 요소에 의해 선택된다.
-<p>우리의 대화식 글자 크기 버튼들은 body 요소의 font-size 속성을 변경할 수 있고 이런 조정은 상대적 단위들 덕분에 페이지의 다른 요소에 의해 선택된다.</p>
+여기 자바스크립트 코드가 있다.
-<p>여기 자바스크립트 코드가 있다.</p>
+```js
+ function makeSizer(size) {
+ return function() {
+ document.body.style.fontSize = size + 'px';
+ };
+ }
-<pre>function makeSizer(size) {
- return function() {
- document.body.style.fontSize = size + 'px';
- };
-}
+ var size12 = makeSizer(12);
+ var size14 = makeSizer(14);
+ var size16 = makeSizer(16);
+```
-var size12 = makeSizer(12);
-var size14 = makeSizer(14);
-var size16 = makeSizer(16);
-</pre>
+`size12`, `size14`, `size16`은 body 요소의 글자 크기를 각각 12, 14, 16 픽셀로 바꾸는 함수이다. 이 함수들을 아래처럼 버튼들에(이 경우에는 링크) 연결할 수 있다.
-<p><code>size12</code>, <code>size14</code>, <code>size16</code>은 body 요소의 글자 크기를 각각 12, 14, 16 픽셀로 바꾸는 함수이다. 이 함수들을 아래처럼 버튼들에(이 경우에는 링크) 연결할 수 있다.</p>
+```js
+ document.getElementById('size-12').onclick = size12;
+ document.getElementById('size-14').onclick = size14;
+ document.getElementById('size-16').onclick = size16;
+```
-<pre>document.getElementById('size-12').onclick = size12;
-document.getElementById('size-14').onclick = size14;
-document.getElementById('size-16').onclick = size16;
-</pre>
+```html
+ <a href="#" id="size-12">12</a>
+ <a href="#" id="size-14">14</a>
+ <a href="#" id="size-16">16</a>
+```
-<pre>&lt;a href="#" id="size-12"&gt;12&lt;/a&gt;
-&lt;a href="#" id="size-14"&gt;14&lt;/a&gt;
-&lt;a href="#" id="size-16"&gt;16&lt;/a&gt;
-</pre>
+{{JSFiddleEmbed("https://jsfiddle.net/vnkuZ/","","200")}}
-<p>{{JSFiddleEmbed("https://jsfiddle.net/vnkuZ/","","200")}}</p>
+다음 링크로 실행해보자. [JSFiddle](https://jsfiddle.net/vnkuZ/7726/)
+## 클로저를 이용해서 프라이빗 메소드 (private method) 흉내내기
-<h2 id="클로저를_이용해서_프라이빗_메소드_private_method_흉내내기">클로저를 이용해서 프라이빗 메소드 (private method) 흉내내기</h2>
+자바와 같은 몇몇 언어들은 메소드를 프라이빗으로 선언할 수 있는 기능을 제공한다. 이는 같은 클래스 내부의 다른 메소드에서만 그 메소드들을 호출할 수 있다는 의미이다.
-<p>자바와 같은 몇몇 언어들은 메소드를 프라이빗으로 선언할 수 있는 기능을 제공한다. 이는 같은 클래스 내부의 다른 메소드에서만 그 메소드들을 호출할 수 있다는 의미이다.</p>
+자바스크립트는 태생적으로는 이런 방법을 제공하지 않지만 클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능하다. 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 관리하는 강력한 방법을 제공하여 불필요한 메소드가 공용 인터페이스를 혼란스럽게 만들지 않도록 한다.
-<p>자바스크립트는 태생적으로는 이런 방법을 제공하지 않지만 클로저를 이용하여 프라이빗 메소드를 흉내내는 것이 가능하다. 프라이빗 메소드는 코드에 제한적인 접근만을 허용한다는 점 뿐만 아니라 전역 네임 스페이스를 관리하는 강력한 방법을 제공하여 불필요한 메소드가 공용 인터페이스를 혼란스럽게 만들지 않도록 한다.</p>
+아래 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 정의하기 위해 클로저를 사용하는 방법을 보여준다. 이렇게 클로저를 사용하는 것을 [모듈 패턴](https://www.google.com/search?q=javascript+module+pattern)이라 한다.
-<p>아래 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 정의하기 위해 클로저를 사용하는 방법을 보여준다. 이렇게 클로저를 사용하는 것을 <a href="http://www.google.com/search?q=javascript+module+pattern">모듈 패턴</a>이라 한다.</p>
+```js
+ var counter = (function() {
+ var privateCounter = 0;
+ function changeBy(val) {
+ privateCounter += val;
+ }
+ return {
+ increment: function() {
+ changeBy(1);
+ },
+ decrement: function() {
+ changeBy(-1);
+ },
+ value: function() {
+ return privateCounter;
+ }
+ };
+ })();
+
+ console.log(counter.value()); // logs 0
+ counter.increment();
+ counter.increment();
+ console.log(counter.value()); // logs 2
+ counter.decrement();
+ console.log(counter.value()); // logs 1
+```
+
+이전 예제에서 각 클로저들이 고유한 문법적 환경을 가졌지만 여기서 우리는 `counter.increment`, `counter.decrement`, `counter.value `세 함수에 의해 공유되는 하나의 어휘적 환경을 만든다.
+
+공유되는 어휘적 환경은 실행되는 익명 함수 안에서 만들어진다. 이 익명 함수는 정의되는 즉시 실행된다. 이 어휘적 환경은 두 개의 프라이빗 아이템을 포함한다. 하나는 `privateCounter`라는 변수이고 나머지 하나는 `changeBy`라는 함수이다. 둘 다 익명 함수 외부에서 접근될 수 없다. 대신에 익명 래퍼에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근되어야만 한다.
+
+위의 세 가지 퍼블릭 함수는 같은 환경을 공유하는 클로저다. 자바스크립트의 어휘적 유효 범위 덕분에 세 함수 각각 `privateCounter` 변수와 `changeBy` 함수에 접근할 수 있다.
+
+카운터를 생성하는 익명 함수를 정의하고 그 함수를 즉시 호출하고 결과를 `counter` 변수에 할당하는 것을 알아차렸을 것이다. 이 함수를 별도의 변수 `makeCounter` 저장하고 이 변수를 이용해 여러 개의 카운터를 만들 수 있다.
+
+```js
+ var makeCounter = function() {
+ var privateCounter = 0;
+ function changeBy(val) {
+ privateCounter += val;
+ }
+ return {
+ increment: function() {
+ changeBy(1);
+ },
+ decrement: function() {
+ changeBy(-1);
+ },
+ value: function() {
+ return privateCounter;
+ }
+ }
+ };
-<pre>var counter = (function() {
- var privateCounter = 0;
- function changeBy(val) {
- privateCounter += val;
- }
- return {
- increment: function() {
- changeBy(1);
- },
- decrement: function() {
- changeBy(-1);
- },
- value: function() {
- return privateCounter;
- }
- };
-})();
-
-console.log(counter.value()); // logs 0
-counter.increment();
-counter.increment();
-console.log(counter.value()); // logs 2
-counter.decrement();
-console.log(counter.value()); // logs 1
-</pre>
-
-<p>이전 예제에서 각 클로저들이 고유한 문법적 환경을 가졌지만 여기서 우리는 <code>counter.increment</code>, <code>counter.decrement</code>, <code>counter.value </code>세 함수에 의해 공유되는 하나의 어휘적 환경을 만든다.</p>
-
-<p>공유되는 어휘적 환경은 실행되는 익명 함수 안에서 만들어진다. 이 익명 함수는 정의되는 즉시 실행된다. 이 어휘적 환경은 두 개의 프라이빗 아이템을 포함한다. 하나는 <code>privateCounter</code>라는 변수이고 나머지 하나는 <code>changeBy</code>라는 함수이다. 둘 다 익명 함수 외부에서 접근될 수 없다. 대신에 익명 래퍼에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근되어야만 한다.</p>
-
-<p>위의 세 가지 퍼블릭 함수는 같은 환경을 공유하는 클로저다. 자바스크립트의 어휘적 유효 범위 덕분에 세 함수 각각 <code>privateCounter</code> 변수와 <code>changeBy</code> 함수에 접근할 수 있다.</p>
-
-<p>카운터를 생성하는 익명 함수를 정의하고 그 함수를 즉시 호출하고 결과를 counter 변수에 할당하는 것을 알아차렸을 것이다. 이 함수를 별도의 변수 makeCounter 저장하고 이 변수를 이용해 여러 개의 카운터를 만들 수 있다.</p>
-
-<pre>var makeCounter = function() {
- var privateCounter = 0;
- function changeBy(val) {
- privateCounter += val;
- }
- return {
- increment: function() {
- changeBy(1);
- },
- decrement: function() {
- changeBy(-1);
- },
- value: function() {
- return privateCounter;
+ var counter1 = makeCounter();
+ var counter2 = makeCounter();
+ alert(counter1.value()); /* 0 */
+ counter1.increment();
+ counter1.increment();
+ alert(counter1.value()); /* 2 */
+ counter1.decrement();
+ alert(counter1.value()); /* 1 */
+ alert(counter2.value()); /* 0 */
+```
+
+두 개의 카운터가 어떻게 다른 카운터와 독립성을 유지하는지 주목해보자. 각 클로저는 그들 고유의 클로저를 통한 `privateCounter` 변수의 다른 버전을 참조한다. 각 카운터가 호출될 때마다; 하나의 클로저에서 변수 값을 변경해도 다른 클로저의 값에는 영향을 주지 않는다.
+
+> **참고:** 이런 방식으로 클로저를 사용하여 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 이점들을 얻을 수 있다.
+
+## 클로저 스코프 체인
+
+모든 클로저에는 세가지 스코프(범위)가 있다:
+
+- 지역 범위 (Local Scope, Own scope)
+- 외부 함수 범위 (Outer Functions Scope)
+- 전역 범위 (Global Scope)
+
+따라서, 우리는 클로저에 대해 세가지 범위 모두 접근할 수 있지만, 중첩된 내부 함수가 있는 경우 종종 실수를 저지른다. 아래 예제를 확인해보자:
+
+```js
+ // 전역 범위 (global scope)
+ var e = 10;
+ function sum(a){
+ return function(b){
+ return function(c){
+ // 외부 함수 범위 (outer functions scope)
+ return function(d){
+ // 지역 범위 (local scope)
+ return a + b + c + d + e;
+ }
+ }
+ }
}
- }
-};
-
-var counter1 = makeCounter();
-var counter2 = makeCounter();
-alert(counter1.value()); /* 0 */
-counter1.increment();
-counter1.increment();
-alert(counter1.value()); /* 2 */
-counter1.decrement();
-alert(counter1.value()); /* 1 */
-alert(counter2.value()); /* 0 */
-</pre>
-
-<p>두 개의 카운터가 어떻게 다른 카운터와 독립성을 유지하는지 주목해보자. 각 클로저는 그들 고유의 클로저를 통한 privateCounter 변수의 다른 버전을 참조한다. 각 카운터가 호출될 때마다; 하나의 클로저에서 변수 값을 변경해도 다른 클로저의 값에는 영향을 주지 않는다.</p>
-
-<p>이런 방식으로 클로저를 사용하여 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 이점들을 얻을 수 있다.</p>
-
-<h2 id="클로저_스코프_체인">클로저 스코프 체인</h2>
-
-<p>모든 클로저에는 세가지 스코프(범위)가 있다:-</p>
-
-<ul>
- <li>지역 범위 (Local Scope, Own scope)</li>
- <li>외부 함수 범위 (Outer Functions Scope)</li>
- <li>전역 범위 (Global Scope)</li>
-</ul>
-
-<p>따라서, 우리는 클로저에 대해 세가지 범위 모두 접근할 수 있지만, 중첩된 내부 함수가 있는 경우 종종 실수를 저지른다. 아래 예제를 확인해보자:</p>
-
-<pre><code>// 전역 범위 (global scope)
-var e = 10;
-function sum(a){
- return function(b){
- return function(c){
- // 외부 함수 범위 (outer functions scope)
- return function(d){
- // 지역 범위 (local scope)
- return a + b + c + d + e;
+
+ console.log(sum(1)(2)(3)(4)); // log 20
+
+ // 익명 함수 없이 작성할 수도 있다.
+
+ // 전역 범위 (global scope)
+ var e = 10;
+ function sum(a){
+ return function sum2(b){
+ return function sum3(c){
+ // 외부 함수 범위 (outer functions scope)
+ return function sum4(d){
+ // 지역 범위 (local scope)
+ return a + b + c + d + e;
+ }
+ }
}
}
- }
-}
-console.log(sum(1)(2)(3)(4)); // log 20
+ var s = sum(1);
+ var s1 = s(2);
+ var s2 = s1(3);
+ var s3 = s2(4);
+ console.log(s3) //log 20
+```
-// 익명 함수 없이 작성할 수도 있다.
+위의 예제를 보면 일련의 중첩된 함수들을 확인할 수 있다. 이 함수들은 전부 외부 함수의 스코프에 접근할 수 있다. 그런데 문제는 즉각적인 외부 함수의 스코프만을 추측 한다는 것이다. 이 문맥에서는 모든 클로저가 선언된 외부 함수의 스코프에 접근한다라고 말할 수 있다.
-// 전역 범위 (global scope)
-var e = 10;
-function sum(a){
- return function sum2(b){
- return function sum3(c){
- // 외부 함수 범위 (outer functions scope)
- return function sum4(d){
- // 지역 범위 (local scope)
- return a + b + c + d + e;
- }
- }
- }
-}
+## 루프에서 클로저 생성하기: 일반적인 실수
-var s = sum(1);
-var s1 = s(2);
-var s2 = s1(3);
-var s3 = s2(4);
-console.log(s3) //log 20</code></pre>
+ECMAScript 2015의 [`let`](/en-US/docs/Web/JavaScript/Reference/Statements/let "방해") 키워드 소개 전에는 클로저와 관련된 일반적인 문제는 루프 안에서 클로저가 생성되었을 때 발생한다.다음 예제를 보자.
-<p>위의 예제를 보면 일련의 중첩된 함수들을 확인할 수 있다. 이 함수들은 전부 외부 함수의 스코프에 접근할 수 있다. 그런데 문제는 즉각적인 외부 함수의 스코프만을 추측한다는 것이다. 이 문맥에서는 모든 클로저가 선언된 외부 함수의 스코프에 접근한다라고 말할 수 있다.</p>
+```html
+ <p id="help">Helpful notes will appear here</p>
+ <p>E-mail: <input type="text" id="email" name="email"></p>
+ <p>Name: <input type="text" id="name" name="name"></p>
+ <p>Age: <input type="text" id="age" name="age"></p>
+```
-<h2 id="루프에서_클로저_생성하기_일반적인_실수">루프에서 클로저 생성하기: 일반적인 실수</h2>
+```js
+ function showHelp(help) {
+ document.getElementById('help').innerHTML = help;
+ }
-<p>ECMAScript 2015의 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let" title="방해"><code>let</code> 키워드</a> 소개 전에는 클로저와 관련된 일반적인 문제는 루프 안에서 클로저가 생성되었을 때 발생한다.다음 예제를 보자.</p>
+ function setupHelp() {
+ var helpText = [
+ {'id': 'email', 'help': 'Your e-mail address'},
+ {'id': 'name', 'help': 'Your full name'},
+ {'id': 'age', 'help': 'Your age (you must be over 16)'}
+ ];
+
+ for (var i = 0; i < helpText.length; i++) {
+ var item = helpText[i];
+ document.getElementById(item.id).onfocus = function() {
+ showHelp(item.help);
+ }
+ }
+ }
-<pre>&lt;p id="help"&gt;Helpful notes will appear here&lt;/p&gt;
-&lt;p&gt;E-mail: &lt;input type="text" id="email" name="email"&gt;&lt;/p&gt;
-&lt;p&gt;Name: &lt;input type="text" id="name" name="name"&gt;&lt;/p&gt;
-&lt;p&gt;Age: &lt;input type="text" id="age" name="age"&gt;&lt;/p&gt;
-</pre>
+ setupHelp();
+```
-<pre>function showHelp(help) {
- document.getElementById('help').innerHTML = help;
-}
+{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/", "", 200)}}
-function setupHelp() {
- var helpText = [
- {'id': 'email', 'help': 'Your e-mail address'},
- {'id': 'name', 'help': 'Your full name'},
- {'id': 'age', 'help': 'Your age (you must be over 16)'}
- ];
+`helpText` 배열은 세 개의 도움말 힌트를 정의한다. 각 도움말은 문서의 입력 필드의 ID와 연관된다. 루프를 돌면서 각 입력 필드 ID에 해당하는 엘리먼트의 `onfocus` 이벤트에 관련된 도움말을 보여주는 메소드에 연결한다.
- for (var i = 0; i &lt; helpText.length; i++) {
- var item = helpText[i];
- document.getElementById(item.id).onfocus = function() {
- showHelp(item.help);
- }
- }
-}
+이 코드를 사용하면 제대로 동작하지 않는 것을 알게 된다. 어떤 필드에 포커스를 주더라도 나이에 관한 도움말이 표시된다.
-setupHelp();
-</pre>
+`onfocus` 이벤트에 연결된 함수가 클로저이기 때문이다. 이 클로저는 함수 정의와 `setupHelp` 함수 범위에서 캡처된 환경으로 구성된다. 루프에서 세 개의 클로저가 만들어졌지만 각 클로저는 값이 변하는 변수가 (`item.help`) 있는 같은 단일 환경을 공유한다. `onfocus` 콜백이 실행될 때 콜백의 환경에서 `item` 변수는 (세개의 클로저가 공유한다) `helpText` 리스트의 마지막 요소를 가리키고 있을 것이다.
-<p>{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/", "", 200)}}</p>
+이 경우 한 가지 해결책은 더 많은 클로저를 사용하는 것이다: 특히 앞에서 설명한 함수 팩토리를 사용하는 것이다.
-<p>helpText 배열은 세 개의 도움말 힌트를 정의한다. 각 도움말은 문서의 입력 필드의 ID와 연관된다. 루프를 돌면서 각 입력 필드 ID에 해당하는 엘리먼트의 onfocus 이벤트에 관련된 도움말을 보여주는 메소드에 연결한다.</p>
+```js
+ function showHelp(help) {
+ document.getElementById('help').innerHTML = help;
+ }
-<p>이 코드를 사용하면 제대로 동작하지 않는 것을 알게 된다. 어떤 필드에 포커스를 주더라도 나이에 관한 도움말이 표시된다.</p>
+ function makeHelpCallback(help) {
+ return function() {
+ showHelp(help);
+ };
+ }
-<p>onfocus 이벤트에 연결된 함수가 클로저이기 때문이다. 이 클로저는 함수 정의와 setupHelp 함수 범위에서 캡처된 환경으로 구성된다. 루프에서 세 개의 클로저가 만들어졌지만 각 클로저는 값이 변하는 변수가 (item.help) 있는 같은 단일 환경을 공유한다. onfocus 콜백이 실행될 때 콜백의 환경에서 item 변수는 (세개의 클로저가 공유한다) helpText 리스트의 마지막 요소를 가리키고 있을 것이다.</p>
+ function setupHelp() {
+ var helpText = [
+ {'id': 'email', 'help': 'Your e-mail address'},
+ {'id': 'name', 'help': 'Your full name'},
+ {'id': 'age', 'help': 'Your age (you must be over 16)'}
+ ];
-<p>이 경우 한 가지 해결책은 더 많은 클로저를 사용하는 것이다: 특히 앞에서 설명한 함수 팩토리를 사용하는 것이다.</p>
+ for (var i = 0; i < helpText.length; i++) {
+ var item = helpText[i];
+ document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
+ }
+ }
-<pre>function showHelp(help) {
- document.getElementById('help').innerHTML = help;
-}
+ setupHelp();
+```
-function makeHelpCallback(help) {
- return function() {
- showHelp(help);
- };
-}
+{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/1/", "", 300)}}
-function setupHelp() {
- var helpText = [
- {'id': 'email', 'help': 'Your e-mail address'},
- {'id': 'name', 'help': 'Your full name'},
- {'id': 'age', 'help': 'Your age (you must be over 16)'}
- ];
+이것은 예상대로 동작한다. 모두 단일 환경을 공유하는 콜백대신, `makeHelpCallback` 함수는 각각의 콜백에 새로운 어휘적 환경을 생성한다. 여기서 `help`는 `helpText` 배열의 해당 문자열을 나타낸다.
- for (var i = 0; i &lt; helpText.length; i++) {
- var item = helpText[i];
- document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
- }
-}
+익명 클로저를 사용하여 위 코드를 작성하는 또 다른 방법은 다음과 같다.
-setupHelp();
-</pre>
+```js
+ function showHelp(help) {
+ document.getElementById('help').innerHTML = help;
+ }
-<p>{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/1/", "", 300)}}</p>
+ function setupHelp() {
+ var helpText = [
+ {'id': 'email', 'help': 'Your e-mail address'},
+ {'id': 'name', 'help': 'Your full name'},
+ {'id': 'age', 'help': 'Your age (you must be over 16)'}
+ ];
+
+ for (var i = 0; i < helpText.length; i++) {
+ (function() {
+ var item = helpText[i];
+ document.getElementById(item.id).onfocus = function() {
+ showHelp(item.help);
+ }
+ })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
+ }
+ }
-<p>이것은 예상대로 동작한다. 모두 단일 환경을 공유하는 콜백대신, <code>makeHelpCallback</code> 함수는 각각의 콜백에 새로운 어휘적 환경을 생성한다. 여기서 help는 helpText 배열의 해당 문자열을 나타낸다.</p>
+ setupHelp();
+```
-<p>익명 클로저를 사용하여 위 코드를 작성하는 또 다른 방법은 다음과 같다.</p>
+더 많은 클로저를 사용하는 것이 싫다면 ES2015의 [`let`](/en-US/docs/Web/JavaScript/Reference/Statements/let) 키워드를 사용할 수 있다.
-<pre>function showHelp(help) {
- document.getElementById('help').innerHTML = help;
-}
+```js
+ function showHelp(help) {
+ document.getElementById('help').innerHTML = help;
+ }
-function setupHelp() {
- var helpText = [
- {'id': 'email', 'help': 'Your e-mail address'},
- {'id': 'name', 'help': 'Your full name'},
- {'id': 'age', 'help': 'Your age (you must be over 16)'}
- ];
+ function setupHelp() {
+ var helpText = [
+ {'id': 'email', 'help': 'Your e-mail address'},
+ {'id': 'name', 'help': 'Your full name'},
+ {'id': 'age', 'help': 'Your age (you must be over 16)'}
+ ];
+
+ for (var i = 0; i < helpText.length; i++) {
+ let item = helpText[i];
+ document.getElementById(item.id).onfocus = function() {
+ showHelp(item.help);
+ }
+ }
+ }
- for (var i = 0; i &lt; helpText.length; i++) {
- (function() {
- var item = helpText[i];
- document.getElementById(item.id).onfocus = function() {
- showHelp(item.help);
- }
- })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
- }
-}
+ setupHelp();
+```
-setupHelp();</pre>
+위의 경우 `var` 대신 `let`을 사용하여 모든 클로저가 블록 범위 변수를 바인딩할 것이므로 추가적인 클로저를 사용하지 않아도 완벽하게 동작할 것이다.
-<p>더 많은 클로저를 사용하는 것이 싫다면 ES2015의 <code><a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/let">let</a></code> 키워드를 사용할 수 있다.</p>
+Another alternative could be to use `forEach()` to iterate over the `helpText` array and attach a listener to each [`<input>`](/en-US/docs/Web/HTML/Element/input "The HTML <input> element is used to create interactive controls for web-based forms in order to accept data from the user; a wide variety of types of input data and control widgets are available, depending on the device and user agent."), as shown:
-<pre>function showHelp(help) {
- document.getElementById('help').innerHTML = help;
+```js
+function showHelp(help) {
+ document.getElementById('help').textContent = help;
}
function setupHelp() {
@@ -373,83 +420,86 @@ function setupHelp() {
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
- for (var i = 0; i &lt; helpText.length; i++) {
- let item = helpText[i];
- document.getElementById(item.id).onfocus = function() {
- showHelp(item.help);
+ helpText.forEach(function(text) {
+ document.getElementById(text.id).onfocus = function() {
+ showHelp(text.help);
}
- }
+ });
}
-setupHelp();</pre>
-
-<p>위의 경우 var 대신 let을 사용하여 모든 클로저가 블록 범위 변수를 바인딩할 것이므로 추가적인 클로저를 사용하지 않아도 완벽하게 동작할 것이다.</p>
+setupHelp();
+```
-<h2 id="성능_관련_고려_사항">성능 관련 고려 사항</h2>
+## 성능 관련 고려 사항
-<p>특정 작업에 클로저가 필요하지 않는데 다른 함수 내에서 함수를 불필요하게 작성하는 것은 현명하지 않다. 이것은 처리 속도와 메모리 소비 측면에서 스크립트 성능에 부정적인 영향을 미칠 것이다.</p>
+특정 작업에 클로저가 필요하지 않는데 다른 함수 내에서 함수를 불필요하게 작성하는 것은 현명하지 않다. 이것은 처리 속도와 메모리 소비 측면에서 스크립트 성능에 부정적인 영향을 미칠 것이다.
-<p>예를 들어, 새로운 객체/클래스를 생성 할 때, 메소드는 일반적으로 객체 생성자에 정의되기보다는 객체의 프로토타입에 연결되어야 한다. 그 이유는 생성자가 호출 될 때마다 메서드가 다시 할당되기 때문이다 (즉, 모든 개체가 생성 될 때마다).</p>
+예를 들어, 새로운 객체/클래스를 생성 할 때, 메소드는 일반적으로 객체 생성자에 정의되기보다는 객체의 프로토타입에 연결되어야 한다. 그 이유는 생성자가 호출 될 때 마다 메서드가 다시 할당되기 때문이다 (즉, 모든 개체가 생성 될 때마다).
-<p>비실용적이지만 시범적인 다음 예를 고려하라:</p>
+비실용적이지만 시범적인 다음 예를 고려하라:
-<pre>function MyObject(name, message) {
- this.name = name.toString();
- this.message = message.toString();
- this.getName = function() {
- return this.name;
- };
+```js
+ function MyObject(name, message) {
+ this.name = name.toString();
+ this.message = message.toString();
+ this.getName = function() {
+ return this.name;
+ };
- this.getMessage = function() {
- return this.message;
- };
-}
-</pre>
+ this.getMessage = function() {
+ return this.message;
+ };
+ }
+```
-<p>앞의 코드는 클로저의 이점을 이용하지 않음으로 다음과 같이 다시 쓸 수 있다.</p>
+앞의 코드는 클로저의 이점을 이용하지 않음으로 다음과 같이 다시 쓸 수 있다.
-<pre>function MyObject(name, message) {
- this.name = name.toString();
- this.message = message.toString();
-}
-MyObject.prototype = {
- getName: function() {
- return this.name;
- },
- getMessage: function() {
- return this.message;
- }
-};
-</pre>
-
-<p>그러나 프로토타입을 다시 정의하는 것은 권장되지 않음으로 기존 프로토타입에 추가하는 다음 예제가 더 좋다.</p>
-
-<pre>function MyObject(name, message) {
- this.name = name.toString();
- this.message = message.toString();
-}
-MyObject.prototype.getName = function() {
- return this.name;
-};
-MyObject.prototype.getMessage = function() {
- return this.message;
-};
-</pre>
-
-<p>위의 코드는 같은 결과를 가진 더 깨끗한 방법으로 작성할 수도 있다:</p>
-
-<pre>function MyObject(name, message) {
- this.name = name.toString();
- this.message = message.toString();
-}
-(function() {
- this.getName = function() {
+```js
+ function MyObject(name, message) {
+ this.name = name.toString();
+ this.message = message.toString();
+ }
+ MyObject.prototype = {
+ getName: function() {
return this.name;
- };
- this.getMessage = function() {
+ },
+ getMessage: function() {
return this.message;
+ }
};
-}).call(MyObject.prototype);
-</pre>
+```
-<p>앞의 두 가지 예제에서 상속된 프로토타입은 모든 객체에서 공유될 수 있으며 메소드 정의는 모든 객체 생성시 발생할 필요가 없다. <a href="/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model" title="en-US/docs/JavaScript/Guide/Details of the Object Model">객체 모델의 세부 사항</a>을 참고하라.</p>
+그러나 프로토타입을 다시 정의하는 것은 권장되지 않음으로 기존 프로토타입에 추가하는 다음 예제가 더 좋다.
+
+```js
+ function MyObject(name, message) {
+ this.name = name.toString();
+ this.message = message.toString();
+ }
+ MyObject.prototype.getName = function() {
+ return this.name;
+ };
+ MyObject.prototype.getMessage = function() {
+ return this.message;
+ };
+```
+
+위의 코드는 같은 결과를 가진 더 깨끗한 방법으로 작성할 수도 있다:
+
+```js
+ function MyObject(name, message) {
+ this.name = name.toString();
+ this.message = message.toString();
+ }
+ (function() {
+ this.getName = function() {
+ return this.name;
+ };
+ this.getMessage = function() {
+ return this.message;
+ };
+ }).call(MyObject.prototype);
+```
+
+앞의 두 가지 예제에서 상속된 프로토타입은 모든 객체에서 공유될 수 있으며 메소드 정의는 모든 객체 생성시 발생할 필요가 없다. [객체 모델의 세부 사항](/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model)을
+참고하라.