Skip to content

Commit ce7a22a

Browse files
committed
currying
1 parent 19755ad commit ce7a22a

File tree

6 files changed

+313
-312
lines changed

6 files changed

+313
-312
lines changed

1-js/06-advanced-functions/11-currying-partials/1-ask-currying/solution.md renamed to 1-js/06-advanced-functions/10-bind/6-ask-partial/solution.md

File renamed without changes.

1-js/06-advanced-functions/11-currying-partials/1-ask-currying/task.md renamed to 1-js/06-advanced-functions/10-bind/6-ask-partial/task.md

File renamed without changes.

1-js/06-advanced-functions/10-bind/article.md

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,123 @@ for (let key in user) {
194194
Некоторые JS-библиотеки предоставляют встроенные функции для удобной массовой привязки контекста, например [_.bindAll(obj)](http://lodash.com/docs#bindAll) в lodash.
195195
````
196196
197+
## Частичное применение
198+
199+
До сих пор мы говорили только о привязывании `this`. Давайте шагнём дальше.
200+
201+
Мы можем привязать не только `this`, но и аргументы. Это делается редко, но иногда может быть полезно.
202+
203+
Полный синтаксис `bind`:
204+
205+
```js
206+
let bound = func.bind(context, [arg1], [arg2], ...);
207+
```
208+
Это позволяет привязать контекст `this` и начальные аргументы функции.
209+
210+
Например, у нас есть функция умножения `mul(a, b)`:
211+
212+
```js
213+
function mul(a, b) {
214+
return a * b;
215+
}
216+
```
217+
218+
Давайте воспользуемся `bind`, чтобы создать функцию `double` на её основе:
219+
220+
```js run
221+
function mul(a, b) {
222+
return a * b;
223+
}
224+
225+
*!*
226+
let double = mul.bind(null, 2);
227+
*/!*
228+
229+
alert( double(3) ); // = mul(2, 3) = 6
230+
alert( double(4) ); // = mul(2, 4) = 8
231+
alert( double(5) ); // = mul(2, 5) = 10
232+
```
233+
234+
Вызов `mul.bind(null, 2)` создаёт новую функцию `double`, которая передаёт вызов `mul`, фиксируя `null` как контекст и `2` -- как первый аргумент. Следующие аргументы передаются "как есть".
235+
236+
Это называется [частичное применение](https://ru.wikipedia.org/wiki/Частичное_применение) -- мы создаём новую функцию, фиксируя некоторые из существующих параметров.
237+
238+
Обратите внимание, что в данном случае мы на самом деле не используем `this`. Но для `bind` это обязательный параметр, так что мы должны передать туда что-нибудь вроде `null`.
239+
240+
В следующем коде функция `triple` умножает значение на три:
241+
242+
```js run
243+
function mul(a, b) {
244+
return a * b;
245+
}
246+
247+
*!*
248+
let triple = mul.bind(null, 3);
249+
*/!*
250+
251+
alert( triple(3) ); // = mul(3, 3) = 9
252+
alert( triple(4) ); // = mul(3, 4) = 12
253+
alert( triple(5) ); // = mul(3, 5) = 15
254+
```
255+
256+
Для чего мы обычно создаём частично применённую функцию?
257+
258+
Польза от этого в том, что возможно создать независимую функцию с понятным названием (`double`, `triple`). Мы можем использовать её и не передавать каждый раз первый аргумент, т.к. он зафиксирован с помощью `bind`.
259+
260+
В других случаях частичное применение полезно, когда у нас есть очень общая функция и для удобства мы хотим создать её частный вариант.
261+
262+
Например, у нас есть функция `send(from, to, text)`. Потом внутри объекта `user` мы можем захотеть использовать её частный вариант: `sendTo(to, text)`, который отправляет текст от имени текущего пользователя.
263+
264+
## Частичное применение без контекста
265+
266+
Что если мы хотим зафиксировать некоторые аргументы, но не контекст `this`? Например, для метода объекта.
267+
268+
Встроенный `bind` не позволяет этого. Мы не можем просто опустить контекст и перейти к аргументам.
269+
270+
К счастью, лекго создать вспомогательную функцию `partial`, которая привязывает только аргументы.
271+
272+
Вот так:
273+
274+
```js run
275+
*!*
276+
function partial(func, ...argsBound) {
277+
return function(...args) { // (*)
278+
return func.call(this, ...argsBound, ...args);
279+
}
280+
}
281+
*/!*
282+
283+
// использование:
284+
let user = {
285+
firstName: "John",
286+
say(time, phrase) {
287+
alert(`[${time}] ${this.firstName}: ${phrase}!`);
288+
}
289+
};
290+
291+
// добавляем частично применённый метод с фиксированным временем
292+
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
293+
294+
user.sayNow("Hello");
295+
// Что-то вроде этого:
296+
// [10:00] John: Hello!
297+
```
298+
299+
Результатом вызова `partial(func[, arg1, arg2...])` будет обёртка `(*)`, которая вызывает `func` с:
300+
- Тем же `this`, который она получает (для вызова `user.sayNow` -- это будет `user`)
301+
- Затем передаёт ей `...argsBound` -- аргументы из вызова `partial` (`"10:00"`)
302+
- Затем передаёт ей `...args` -- аргументы, полученные обёрткой (`"Hello"`)
303+
304+
Благодаря оператору расширения `...` это реализовать очень легко, не правда ли?
305+
306+
Также есть готовый вариант [_.partial](https://lodash.com/docs#partial) из библиотеки lodash.
307+
197308
## Итого
198309
199-
Метод `func.bind(context, ...args)` возвращает "связанный вариант" функции `func`, который фиксирует контекст `this` и первые аргументы, если они заданы.
310+
Метод `bind` возвращает "привязанный вариант" функции `func`, фиксируя контекст `this` и первые аргументы `arg1`, `arg2`..., если они заданы.
311+
312+
Обычно `bind` применяется для фиксации `this` в методе объекта, чтобы передать его в качестве колбэка. Например, для `setTimeout`.
313+
314+
Когда мы привязываем аргументы, такая функция называется "частично применённой" или "частичной".
200315
201-
Обычно мы применяем `bind`, для исправления `this` в методе объекта, чтобы мы могли передать его куда-нибудь. Например, в `setTimeout`. В современной разработке существуют и другие причины для "связывания", мы встретимся с ними позже.
316+
Частичное применение удобно, когда мы не хотим повторять один и тот же аргумент много раз. Например, когда у нас есть функция `send(from, to)`, и `from` всё время будет одинаков для нашей задачи, мы можем создать частично применённую функцию и дальше работать с ней.

0 commit comments

Comments
 (0)