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/10-bind/article.md
+117-2Lines changed: 117 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -194,8 +194,123 @@ for (let key in user) {
194
194
Некоторые JS-библиотеки предоставляют встроенные функции для удобной массовой привязки контекста, например [_.bindAll(obj)](http://lodash.com/docs#bindAll) в lodash.
195
195
````
196
196
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
+
197
308
## Итого
198
309
199
-
Метод `func.bind(context, ...args)` возвращает "связанный вариант" функции `func`, который фиксирует контекст `this` и первые аргументы, если они заданы.
310
+
Метод `bind` возвращает "привязанный вариант" функции `func`, фиксируя контекст `this` и первые аргументы `arg1`, `arg2`..., если они заданы.
311
+
312
+
Обычно `bind` применяется для фиксации `this` в методе объекта, чтобы передать его в качестве колбэка. Например, для `setTimeout`.
313
+
314
+
Когда мы привязываем аргументы, такая функция называется "частично применённой" или "частичной".
200
315
201
-
Обычно мы применяем `bind`, для исправления `this` в методе объекта, чтобы мы могли передать его куда-нибудь. Например, в `setTimeout`. В современной разработке существуют и другие причины для "связывания", мы встретимся с ними позже.
316
+
Частичное применение удобно, когда мы не хотим повторять один и тот же аргумент много раз. Например, когда у нас есть функция `send(from, to)`, и `from` всё время будет одинаков для нашей задачи, мы можем создать частично применённую функцию и дальше работать с ней.
0 commit comments