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/07-new-function/article.md
+11-10Lines changed: 11 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,3 +1,4 @@
1
+
1
2
# new Function 문법
2
3
3
4
함수 표현식과 함수 선언문 이외에 함수를 만들 수도 있는 방법이 하나 더 있습니다. 잘 사용하는 방법은 아니지만, 이 방법 외에는 대안이 없을 때 사용합니다.
@@ -7,17 +8,17 @@
7
8
`new Function` 문법을 사용하면 함수를 만들 수 있습니다.
8
9
9
10
```js
10
-
let func =newFunction([arg1, arg2, ...argN], functionBody);
11
+
let func =newFunction([arg1, arg2, ...argN], functionBody);
11
12
```
12
13
13
14
새로 만들어지는 함수는 인수 `arg1...argN`과 함수 본문 `functionBody`로 구성됩니다.
14
15
15
16
인수 두 개가 있는 함수를 직접 만들어 보면서 `new Function` 문법에 대해 이해해보도록 합시다.
16
17
17
18
```js run
18
-
let sum =newFunction("a", "b", "return a + b");
19
+
let sum =newFunction('a', 'b', 'return a + b');
19
20
20
-
alert(sum(1, 2)); // 3
21
+
alert(sum(1, 2)); // 3
21
22
```
22
23
23
24
인수가 없고 함수 본문만 있는 함수를 만들어보겠습니다.
@@ -45,11 +46,11 @@ func();
45
46
46
47
## 클로저
47
48
48
-
함수는 특별한 프로퍼티 `[[Environment]]`에 저장된 정보를 이용해 자기 자신이 태어난 곳을 기억합니다. `[[Environment]]`는 함수가 만들어진 렉시컬 환경을 참조합니다(자세한 내용은 <info:closure>에서 다루었습니다).
49
+
함수는 특별한 프로퍼티 `[[Environment]]`에 저장된 정보를 이용해 자기 자신이 태어난 곳을 기억합니다. `[[Environment]]`는 함수가 만들어진 렉시컬 환경을 참조합니다(자세한 내용은 <info:closure>에서 다루었습니다).
49
50
50
51
그런데 `new Function`을 이용해 함수를 만들면 함수의 `[[Environment]]` 프로퍼티가 현재 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하게 됩니다.
51
52
52
-
따라서 `new Function`을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다.
53
+
따라서 `new Function`을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다.
53
54
54
55
```js run
55
56
functiongetFunc() {
@@ -89,7 +90,7 @@ getFunc()(); // getFunc의 렉시컬 환경에 있는 값 *!*"test"*/!*가 출
89
90
90
91
`new Function`으로 만든 새로운 함수 내부에서 외부 변수에 접근하려 할 때, 기존 함수 선언 방식으로 작성한 함수와 동일한 동작이 보장되어야 하죠.
91
92
92
-
그런데 스크립트가 프로덕션 서버에 반영되기 전, _압축기(minifier)_ 에 의해 압축될 때 문제가 발생합니다. 압축기는 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램인데 압축기가 지역 변수 이름을 짧게 바꾸면서 문제가 발생하죠.
93
+
그런데 스크립트가 프로덕션 서버에 반영되기 전, *압축기(minifier)* 에 의해 압축될 때 문제가 발생합니다. 압축기는 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램인데 압축기가 지역 변수 이름을 짧게 바꾸면서 문제가 발생하죠.
93
94
94
95
구체적으로 어떤 부분이 문제가 되는지 예시를 통해 알아봅시다. 함수 내부에 `let userName`라는 변수가 있으면 이 지역변수는 압축기에 의해 `let a` 등(해당 글자가 이미 사용 중이라면 다른 짧은 이름)으로 대체되는데, 이때 `userName` 모두가 `a`로 교체됩니다. `userName`은 지역변수이고, 함수 외부에선 함수 내부에 있는 변수에 접근할 수 없기 때문에 이렇게 해도 전혀 문제가 없죠. 압축기는 단순히 찾아바꾸기가 아니라 코드 구조를 분석해 기존 코드의 기능을 망가뜨리지 않으면서 영리하게 제 역할을 수행합니다.
95
96
@@ -106,17 +107,17 @@ getFunc()(); // getFunc의 렉시컬 환경에 있는 값 *!*"test"*/!*가 출
106
107
문법:
107
108
108
109
```js
109
-
let func =newFunction([arg1, arg2, ...argN], functionBody);
110
+
let func =newFunction([arg1, arg2, ...argN], functionBody);
110
111
```
111
112
112
113
인수를 한꺼번에 모아(쉼표로 구분) 전달할 수도 있습니다.
113
114
114
115
아래 세 선언 방식은 동일하게 동작하죠.
115
116
116
117
```js
117
-
newFunction("a", "b", "return a + b"); // 기본 문법
118
-
newFunction("a,b", "return a + b"); // 쉼표로 구분
119
-
newFunction("a , b", "return a + b"); // 쉼표와 공백으로 구분
118
+
newFunction('a', 'b', 'return a + b'); // 기본 문법
119
+
newFunction('a,b', 'return a + b'); // 쉼표로 구분
120
+
newFunction('a , b', 'return a + b'); // 쉼표와 공백으로 구분
120
121
```
121
122
122
123
`new Function`을 이용해 만든 함수의 `[[Environment]]`는 외부 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하므로 외부 변수를 사용할 수 없습니다. 단점 같아 보이는 특징이긴 하지만 에러를 예방해 준다는 관점에선 장점이 되기도 합니다. 구조상으론 매개변수를 사용해 값을 받는 게 더 낫습니다. 압축기에 의한 에러도 방지할 수 있죠.
Chrome과 Firefox를 포함한 대부분의 브라우저는`alert/confirm/prompt` 창이 떠 있는 동안에도 내부 타이머를 멈추지 않습니다.
129
+
```smart header="`alert`창이 떠 있더라도 타이머는 멈추지 않습니다."
130
+
Chrome과 Firefox를 포함한 대부분의 브라우저는`alert/confirm/prompt` 창이 떠 있는 동안에도 내부 타이머를 멈추지 않습니다.
134
131
135
132
위 예시를 실행하고 첫 번째 `alert` 창이 떴을 때 몇 초간 기다렸다가 창을 닫으면, 두 번째 `alert` 창이 바로 나타나는 것을 보고 이를 확인할 수 있습니다. 이런 이유로 얼럿 창은 명시한 지연 시간인 2초보다 더 짧은 간격으로 뜨게 됩니다.
136
-
137
-
````
133
+
```
138
134
139
135
## 중첩 setTimeout
140
136
@@ -153,7 +149,7 @@ let timerId = setTimeout(function tick() {
153
149
timerId = setTimeout(tick, 2000); // (*)
154
150
*/!*
155
151
}, 2000);
156
-
````
152
+
```
157
153
158
154
다섯 번째 줄의 `setTimeout`은 `(*)`로 표시한 줄의 실행이 종료되면 다음 호출을 스케줄링합니다.
159
155
@@ -162,7 +158,6 @@ let timerId = setTimeout(function tick() {
162
158
5초 간격으로 서버에 요청을 보내 데이터를 얻는다고 가정해 봅시다. 서버가 과부하 상태라면 요청 간격을 10초, 20초, 40초 등으로 증가시켜주는 게 좋을 겁니다.
163
159
164
160
아래는 이를 구현한 의사 코드입니다.
165
-
166
161
```js
167
162
let delay =5000;
168
163
@@ -179,6 +174,7 @@ let timerId = setTimeout(function request() {
179
174
}, delay);
180
175
```
181
176
177
+
182
178
CPU 소모가 많은 작업을 주기적으로 실행하는 경우에도 `setTimeout`을 재귀 실행하는 방법이 유용합니다. 작업에 걸리는 시간에 따라 다음 작업을 유동적으로 계획할 수 있기 때문입니다.
183
179
184
180
**중첩 `setTimeout`을 이용하는 방법은 지연 간격을 보장하지만 `setInterval`은 이를 보장하지 않습니다.**
@@ -187,7 +183,7 @@ CPU 소모가 많은 작업을 주기적으로 실행하는 경우에도 `setTim
187
183
188
184
```js
189
185
let i =1;
190
-
setInterval(function() {
186
+
setInterval(function() {
191
187
func(i++);
192
188
}, 100);
193
189
```
@@ -214,7 +210,7 @@ setTimeout(function run() {
214
210
215
211
그렇다면 `func`을 실행하는 데 걸리는 시간이 명시한 지연 간격보다 길 때 어떤 일이 발생할까요?
216
212
217
-
이런 경우는 엔진이 `func`의 실행이 종료될 때까지 기다려줍니다. `func`의 실행이 종료되면 엔진은 스케줄러를 확인하고, 지연 시간이 지났으면 다음 호출을 _바로_ 시작합니다.
213
+
이런 경우는 엔진이 `func`의 실행이 종료될 때까지 기다려줍니다. `func`의 실행이 종료되면 엔진은 스케줄러를 확인하고, 지연 시간이 지났으면 다음 호출을 *바로* 시작합니다.
218
214
219
215
따라서 함수 호출에 걸리는 시간이 매번 `delay` 밀리초보다 길면, 모든 함수가 쉼 없이 계속 연속 호출됩니다.
220
216
@@ -260,7 +256,7 @@ alert("Hello");
260
256
대기 시간이 0인 setTimeout을 활용한 브라우저 환경에서의 유스 케이스는 <info:event-loop>에서 자세히 다루도록 하겠습니다.
261
257
262
258
````smart header="실제로 지연시간이 0인 경우는 없습니다(브라우저 환경)."
263
-
브라우저는 [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 "다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다."라는 제약이 명시되어 있습니다.
259
+
브라우저는 [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers)에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 "다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다."라는 제약을 명시합니다.
264
260
265
261
예시를 보며 이 제약 사항을 이해해봅시다. 예시 내 `setTimeout`은 지연 없이 함수 run을 다시 호출할 수 있게 스케줄링 되어 있습니다. 배열 `times`에는 실제 지연 간격에 대한 정보가 기록되도록 해놓았는데, 배열 times에 어떤 값이 저장되는지 알아봅시다.
Copy file name to clipboardExpand all lines: 1-js/06-advanced-functions/09-call-apply-decorators/article.md
+26-26Lines changed: 26 additions & 26 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# call/apply와 데코레이터, 포워딩
2
2
3
-
자바스크립트는 함수를 다룰 때 탁월한 유연성을 제공합니다. 함수는 이곳저곳 전달될 수 있고, 객체로도 사용될 수 있습니다. 이번 챕터에선 함수 간에 호출을 어떻게 _포워딩(forwarding)_ 하는지, 함수를 어떻게 _데코레이팅(decorating)_ 하는지에 대해 알아보겠습니다.
3
+
자바스크립트는 함수를 다룰 때 탁월한 유연성을 제공합니다. 함수는 이곳저곳 전달될 수 있고, 객체로도 사용될 수 있습니다. 이번 챕터에선 함수 간에 호출을 어떻게 *포워딩(forwarding)* 하는지, 함수를 어떻게 *데코레이팅(decorating)* 하는지에 대해 알아보겠습니다.
alert( "다시 호출: "+slow(2) ); // 캐시에서 slow(2)의 결과를 가져옵니다.
45
44
```
46
45
47
-
`cachingDecorator`같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 _데코레이터(decorator)_ 라고 부릅니다.
46
+
`cachingDecorator`같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 *데코레이터(decorator)* 라고 부릅니다.
48
47
49
48
모든 함수를 대상으로 `cachingDecorator`를 호출 할 수 있는데, 이때 반환되는 것은 캐싱 래퍼입니다. 함수에 `cachingDecorator`를 적용하기만 하면 캐싱이 가능한 함수를 원하는 만큼 구현할 수 있기 때문에 데코레이터 함수는 아주 유용하게 사용됩니다.
50
49
51
-
캐싱 관련 코드를 함수 코드와 분리할 수 있기 때문에 함수의 코드가 간결해진다는 장점도 있습니다.
50
+
캐싱 관련 코드를 함수 코드와 분리할 수 있기 때문에 함수의 코드가 간결해진다는 장점도 있습니다.
52
51
53
52
아래 그림에서 볼 수 있듯이 `cachingDecorator(func)`를 호출하면 '래퍼(wrapper)', `function(x)`이 반환됩니다. 래퍼 `function(x)`는 `func(x)`의 호출 결과를 캐싱 로직으로 감쌉니다(wrapping).
54
53
@@ -59,7 +58,7 @@ alert( "다시 호출: " + slow(2) ); // 캐시에서 slow(2)의 결과를 가
59
58
`slow` 본문을 수정하는 것 보다 독립된 래퍼 함수 `cachingDecorator`를 사용할 때 생기는 이점을 정리하면 다음과 같습니다.
60
59
61
60
-`cachingDecorator`를 재사용 할 수 있습니다. 원하는 함수 어디에든 `cachingDecorator`를 적용할 수 있습니다.
62
-
- 캐싱 로직이 분리되어 `slow` 자체의 복잡성이 증가하지 않습니다.
61
+
- 캐싱 로직이 분리되어 `slow` 자체의 복잡성이 증가하지 않습니다.
63
62
- 필요하다면 여러 개의 데코레이터를 조합해서 사용할 수도 있습니다(추가 데코레이터는 `cachingDecorator` 뒤를 따릅니다).
0 commit comments