Skip to content

[재작업] 1-js/06-advanced-functions/09-call-apply-decorators 원문변경 #325

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

Closed
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
@@ -1 +1 @@
The wrapper returned by `spy(f)` should store all arguments and then use `f.apply` to forward the call.
`spy(f)`에 의해 반환된 래퍼는 모든 인수를 저장한 다음 `f.apply`를 사용하여 호출을 전달해야 합니다.
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ importance: 5

---

# Spy decorator
# 스파이 데코레이터

Create a decorator `spy(func)` that should return a wrapper that saves all calls to function in its `calls` property.
`calls` 프로퍼티에 함수에 대한 모든 호출을 저장한 래퍼를 반환하는 데코레이터 `spy(func)`를 만들어 보세요.

Every call is saved as an array of arguments.
모든 호출은 인수 배열로 저장되어야 합니다.

For instance:
예를 들면

```js
function work(a, b) {
alert( a + b ); // work is an arbitrary function or method
alert( a + b ); // work 함수는 임의의 함수거나 메서드 입니다.
}

*!*
Expand All @@ -27,4 +27,4 @@ for (let args of work.calls) {
}
```

P.S. That decorator is sometimes useful for unit-testing. Its advanced form is `sinon.spy` in [Sinon.JS](http://sinonjs.org/) library.
P.S. 이러한 데코레이터는 때때로 단위 테스트에 유용합니다. 고급적인 형식은 [Sinon.JS](http://sinonjs.org/) 라이브러리에 있는 `sinon.spy`가 있습니다.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The solution:
해답

```js run demo
function delay(f, ms) {
Expand All @@ -11,22 +11,22 @@ function delay(f, ms) {

let f1000 = delay(alert, 1000);

f1000("test"); // shows "test" after 1000ms
f1000("test"); // 1000 밀리초 후에 "test"를 보여줌
```

Please note how an arrow function is used here. As we know, arrow functions do not have own `this` and `arguments`, so `f.apply(this, arguments)` takes `this` and `arguments` from the wrapper.
위의 답안에서 어떻게 화살표 함수가 사용되었는지 주목하세요. 이미 살펴보았듯이 화살표 함수는 자체적으로 `this``arguments`가 없습니다. 그래서 `f.apply(this, arguments)``this``arguments`를 래퍼로부터 가져올 수 있는 것이죠.

If we pass a regular function, `setTimeout` would call it without arguments and `this=window` (assuming we're in the browser).
일반 함수로 전달했다면 `setTimeout`은 인수가 없고 `this=window`로 호출되었을 것입니다(브라우저 안이라고 가정해서).

We still can pass the right `this` by using an intermediate variable, but that's a little bit more cumbersome:
일반 함수를 사용해도 여전히 중간 변수를 사용하여 올바른 `this`를 전달할 수 있지만 조금 더 신경써야할 부분이 많습니다.

```js
function delay(f, ms) {

return function(...args) {
let savedThis = this; // store this into an intermediate variable
let savedThis = this; // this를 중간 변수로 저장합니다.
setTimeout(function() {
f.apply(savedThis, args); // use it here
f.apply(savedThis, args); // 여기서 중간 변수로 저장된 this를 사용합니다.
}, ms);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@ importance: 5

---

# Delaying decorator
# 지연 데코레이터

Create a decorator `delay(f, ms)` that delays each call of `f` by `ms` milliseconds.
`delay(f, ms)`라는 각각의 `f` 호출을 `ms` 밀리초 만큼 지연하는 데코레이터를 작성해보세요.

For instance:
예를 들면

```js
function f(x) {
alert(x);
}

// create wrappers
// 래퍼를 생성합니다.
let f1000 = delay(f, 1000);
let f1500 = delay(f, 1500);

f1000("test"); // shows "test" after 1000ms
f1500("test"); // shows "test" after 1500ms
f1000("test"); // 1000 밀리초 후에 "test"를 보여줍니다.
f1500("test"); // 1500 밀리초 후에 "test"를 보여줍니다.
```

In other words, `delay(f, ms)` returns a "delayed by `ms`" variant of `f`.
다르게 표현하면, `delay(f, ms)` 함수는 `f`함수를 `ms`만큼 지연된 변형된 형태로 반환합니다.

In the code above, `f` is a function of a single argument, but your solution should pass all arguments and the context `this`.
위의 코드에서 `f`는 단일 인수의 함수이지만 솔루션은 모든 인수와 컨텍스트 `this`를 전달해야 합니다.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ function debounce(f, ms) {
}
```

A call to `debounce` returns a wrapper. There may be two states:
`debounce`를 호출하면 래퍼를 반환합니다. 여기에는 두 가지 상태가 있습니다.

- `isCooldown = false` -- ready to run.
- `isCooldown = true` -- waiting for the timeout.
- `isCooldown = false` -- 실행시킬 준비가 되었습니다.
- `isCooldown = true` -- 시간이 지날동안 기다립니다.

In the first call `isCooldown` is falsy, so the call proceeds, and the state changes to `true`.
첫번째 `isCoolDown`은 허위로 호출됩니다. 그래서 호출이 되면 상태는 `true`로 바뀌는 것이죠.

While `isCooldown` is true, all other calls are ignored.
`isCooldown`이 true일 동안은 다른 호출은 무시됩니다.

Then `setTimeout` reverts it to `false` after the given delay.
그리고 `setTimeout` 함수가 주어진 지연시간이 지난 후에 상태를 `false`로 바꾸게 됩니다.
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ importance: 5

---

# Debounce decorator
# 디바운스 데코레이터

The result of `debounce(f, ms)` decorator should be a wrapper that passes the call to `f` at maximum once per `ms` milliseconds.
`debounce(f, ms)`데코레이터는 `ms` 밀리초 마다 최댓값에 한번 `f` 에 호출을 전달하는 래퍼이어야 합니다.

In other words, when we call a "debounced" function, it guarantees that all future calls to the function made less than `ms` milliseconds after the previous call will be ignored.
다르게 표현하면 "debounced" 함수를 호출하면 `ms` 밀리초 미만의 함수에 대한 다른 함수호출은 이전 함수 호출 후 무시돼야 합니다.

For instance:
예를 들면

```js no-beautify
let f = debounce(alert, 1000);

f(1); // runs immediately
f(2); // ignored
f(1); // 바로 실행됩니다
f(2); // 무시됩니다.

setTimeout( () => f(3), 100); // ignored ( only 100 ms passed )
setTimeout( () => f(4), 1100); // runs
setTimeout( () => f(5), 1500); // ignored (less than 1000 ms from the last run)
setTimeout( () => f(3), 100); // 무시됨 ( 100ms밖에 지나지 않았음 )
setTimeout( () => f(4), 1100); // 실행됨
setTimeout( () => f(5), 1500); // 무시됨 (마지막 실행 후에 1000ms 가 지나지 않았음)
```

In practice `debounce` is useful for functions that retrieve/update something when we know that nothing new can be done in such a short period of time, so it's better not to waste resources.
실제로 `debounce`는 검색/업데이트하는 함수가 짧은 시간 내에 새로운 작업을 진행될 수 없다는 것을 알고 있을 때 유용하므로 자원을 낭비하지 않는 것이 좋습니다.
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ function throttle(func, ms) {
}
```

A call to `throttle(func, ms)` returns `wrapper`.
`throttle(func, ms)`에 대한 호출은`wrapper`를 반환합니다.

1. During the first call, the `wrapper` just runs `func` and sets the cooldown state (`isThrottled = true`).
2. In this state all calls memorized in `savedArgs/savedThis`. Please note that both the context and the arguments are equally important and should be memorized. We need them simultaneously to reproduce the call.
3. ...Then after `ms` milliseconds pass, `setTimeout` triggers. The cooldown state is removed (`isThrottled = false`). And if we had ignored calls, then `wrapper` is executed with last memorized arguments and context.
1. 첫 번째 호출 중에 `wrapper``func`를 실행하고 cooldown의 상태를 설정합니다(`isThrottled = true`).
2. 상태가 설정되고 모든 호출은 `savedArgs/savedThis`에 기억됩니다. 컨텍스트와 인수는 똑같이 중요하며 기억돼야 합니다. 호출을 재현하기 위해 컨텍스트와 인수가 동시에 필요합니다.
3. ...`ms` 밀리초가 지나면 `setTimeout`이 트리거 되고 cooldown의 상태는 제거됩니다(`isThrottled = false`). 그리고 호출이 무시된다면 `wrapper`는 마지막에 기억된 컨텍스트와 인수와 함께 실행됩니다.

The 3rd step runs not `func`, but `wrapper`, because we not only need to execute `func`, but once again enter the cooldown state and setup the timeout to reset it.
3번째 단계는 `func`가 아니라 `wrapper`로 실행됩니다. 왜냐하면 `func`만을 실행하는 것이 아니라 cooldown 상태를 입력하고 timeout을 설정해서 cooldown을 리셋할 수 있게 해야 되기 때문입니다.
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,47 @@ importance: 5

---

# Throttle decorator
# Throttle 데코레이터

Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper, passing the call to `f` at maximum once per `ms` milliseconds. Those calls that fall into the "cooldown" period, are ignored.
"throttling" 데코레이터라는 `ms` 밀리초가 지난 후 호출을 `f`로 보내는 래퍼를 반환하는 `throttle(f, ms)`를 만들어 봅시다. "cooldown" 시간이 지나면 호출은 무시될 것입니다.

**The difference with `debounce` -- if an ignored call is the last during the cooldown, then it executes at the end of the delay.**
**`debounce`와의 차이점은 cooldown 중에 무시된 호출이 마지막이면 지연이 끝날 때 실행됩니다.**

Let's check the real-life application to better understand that requirement and to see where it comes from.
실제 응용 프로그램을 확인하여 해당 요구 사항을 더 잘 이해하고 어디에서 왔는지 확인합시다.

**For instance, we want to track mouse movements.**
**예를 들어 마우스 움직임을 추적하려고 합니다.**

In browser we can setup a function to run at every mouse movement and get the pointer location as it moves. During an active mouse usage, this function usually runs very frequently, can be something like 100 times per second (every 10 ms).
브라우저에서 우리는 모든 마우스 움직임에서 실행되고 포인터가 움직일 때 포인터 위치를 얻는 기능을 설정할 수 있습니다. 마우스를 사용하는 동안 이 기능은 일반적으로 매우 자주 실행되며 초당 100회 (매 10ms) 정도일 수 있습니다.

**We'd like to update some information on the web-page when the pointer moves.**
**포인터가 움직일 때 웹 페이지의 일부 정보를 업데이트하려고 합니다.**

...But updating function `update()` is too heavy to do it on every micro-movement. There is also no sense in updating more often than once per 100ms.
...그런데 `update()`라는 함수가 미세한 움직임에 매번 업데이트하기에는 너무 무거워 보입니다. 100ms에 한번 보다 더 업데이트하는 건 무의미해 보입니다.

So we'll wrap it into the decorator: use `throttle(update, 100)` as the function to run on each mouse move instead of the original `update()`. The decorator will be called often, but forward the call to `update()` at maximum once per 100ms.
그래서 원래의 `update()` 함수 대신에 매번 마우스를 움직일 때마다 동작하는 `throttle(update, 100)` 데코레이터를 `update()` 함수에 적용해보겠습니다. 이 데코레이터는 자주 호출되지만 `update()`로의 호출 전달을 최대 100ms에 한 번만 하게 될 것입니다.

Visually, it will look like this:
시각적으로 다음과 같습니다.

1. For the first mouse movement the decorated variant immediately passes the call to `update`. That's important, the user sees our reaction to their move immediately.
2. Then as the mouse moves on, until `100ms` nothing happens. The decorated variant ignores calls.
3. At the end of `100ms` -- one more `update` happens with the last coordinates.
4. Then, finally, the mouse stops somewhere. The decorated variant waits until `100ms` expire and then runs `update` with last coordinates. So, quite important, the final mouse coordinates are processed.
1. 첫 번째 마우스 이동의 경우 데코레이터 함수는 즉시 `update` 호출을 전달합니다. 중요한 것은 사용자가 자신의 움직임에 대한 반응을 즉시 볼 수 있다는 것입니다.
2. 마우스가 움직일 때 `100ms`까지 아무 일도 일어나지 않습니다. 데코레이터 함수는 호출을 무시합니다.
3. `100ms`의 마지막에서 마지막 좌표로 `update`함수 호출이 한번더 발생합니다.
4. 마지막으로 마우스가 어딘가에서 멈춥니다. 데코레이터 함수는 `100ms`가 지날 때까지 기다렸다가 마지막 좌표로 `update` 함수를 실행합니다. 그리고 마지막으로 최종 마우스 좌표가 처리됩니다.

A code example:
코드 예시

```js
function f(a) {
console.log(a)
};

// f1000 passes calls to f at maximum once per 1000 ms
// f1000 함수는 f함수에 호출을 최대 1000ms에 한번 전달합니다.
let f1000 = throttle(f, 1000);

f1000(1); // shows 1
f1000(2); // (throttling, 1000ms not out yet)
f1000(3); // (throttling, 1000ms not out yet)
f1000(1); // 1을 보여줍니다.
f1000(2); // (throttling, 1000ms 가 아직 아닙니다.)
f1000(3); // (throttling, 1000ms 가 아직 아닙니다.)

// when 1000 ms time out...
// ...outputs 3, intermediate value 2 was ignored
// 1000 ms 가 지나면...
// ...3을 출력하고, 중간값 2는 무시될 것입니다.
```

P.S. Arguments and the context `this` passed to `f1000` should be passed to the original `f`.
P.S. `f1000`함수로 전달되는 인수와 컨텍스트 `this`는 원래의 `f`함수로 전달되어야 합니다.
Loading