Skip to content

Commit 5f340d0

Browse files
committed
minor
1 parent c8b560d commit 5f340d0

File tree

7 files changed

+312
-306
lines changed

7 files changed

+312
-306
lines changed

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

Lines changed: 2 additions & 294 deletions
Original file line numberDiff line numberDiff line change
@@ -198,300 +198,8 @@ for (let key in user) {
198198
JavaScript libraries also provide functions for convenient mass binding , e.g. [_.bindAll(obj)](http://lodash.com/docs#bindAll) in lodash.
199199
````
200200
201-
202-
203-
## Partial application
204-
205-
Till now we were only talking about binding `this`. Now let's make a step further.
206-
207-
We can bind not only `this`, but also arguments. That's rarely done, but sometimes can be handy.
208-
209-
The full syntax of `bind`:
210-
211-
```js
212-
let bound = func.bind(context, arg1, arg2, ...);
213-
```
214-
215-
It allows to bind context as `this` and starting arguments of the function.
216-
217-
For instance, we have a multiplication function `mul(a, b)`:
218-
219-
```js
220-
function mul(a, b) {
221-
return a * b;
222-
}
223-
```
224-
225-
Let's use `bind` to create a function `double` on its base:
226-
227-
```js run
228-
*!*
229-
let double = mul.bind(null, 2);
230-
*/!*
231-
232-
alert( double(3) ); // = mul(2, 3) = 6
233-
alert( double(4) ); // = mul(2, 4) = 8
234-
alert( double(5) ); // = mul(2, 5) = 10
235-
```
236-
237-
The call to `mul.bind(null, 2)` creates a new function `double` that passes calls to `mul`, fixing `null` as the context and `2` as the first argument. Further arguments are passed "as is".
238-
239-
That's called [partial function application](https://en.wikipedia.org/wiki/Partial_application) -- we create a new function by fixing some parameters of the existing one.
240-
241-
Please note that here we actually don't use `this` here. But `bind` requires it, so we must put in something like `null`.
242-
243-
The function `triple` in the code below triples the value:
244-
245-
```js run
246-
*!*
247-
let triple = mul.bind(null, 3);
248-
*/!*
249-
250-
alert( triple(3) ); // = mul(3, 3) = 9
251-
alert( triple(4) ); // = mul(3, 4) = 12
252-
alert( triple(5) ); // = mul(3, 5) = 15
253-
```
254-
255-
Why do we usually make a partial function?
256-
257-
Here our benefit is that we created an independent function with a readable name (`double`, `triple`). We can use it and don't write the first argument of every time, cause it's fixed with `bind`.
258-
259-
In other cases, partial application is useful when we have a very generic function, and want a less universal variant of it for convenience.
260-
261-
For instance, we have a function `send(from, to, text)`. Then, inside a `user` object we may want to use a partial variant of it: `sendTo(to, text)` that sends from the current user.
262-
263-
### Going partial without context
264-
265-
What if we'd like to fix some arguments, but not bind `this`?
266-
267-
The native `bind` does not allow that. We can't just omit the context and jump to arguments.
268-
269-
Fortunately, a `partial` function for binding only arguments can be easily implemented.
270-
271-
Like this:
272-
273-
```js run
274-
*!*
275-
function partial(func, ...argsBound) {
276-
return function(...args) { // (*)
277-
return func.call(this, ...argsBound, ...args);
278-
}
279-
}
280-
*/!*
281-
282-
// Usage:
283-
let user = {
284-
firstName: "John",
285-
say(time, phrase) {
286-
alert(`[${time}] ${this.firstName}: ${phrase}!`);
287-
}
288-
};
289-
290-
// add a partial method that says something now by fixing the first argument
291-
user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());
292-
293-
user.sayNow("Hello");
294-
// Something like:
295-
// [10:00] Hello, John!
296-
```
297-
298-
The result of `partial(func[, arg1, arg2...])` call is a wrapper `(*)` that calls `func` with:
299-
- Same `this` as it gets (for `user.sayNow` call it's `user`)
300-
- Then gives it `...argsBound` -- arguments from the `partial` call (`"10:00"`)
301-
- Then gives it `...args` -- arguments given to the wrapper (`"Hello"`)
302-
303-
So easy to do it the spread operator, right?
304-
305-
Also there's a ready [_.partial](https://lodash.com/docs#partial) implementation from lodash library.
306-
307-
## Currying
308-
309-
Sometimes people mix up partial function application mentioned above with another thing named "currying". That's another interesting technique of working with functions that we just have to mention here.
310-
311-
[Currying](https://en.wikipedia.org/wiki/Currying) is translating a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`.
312-
313-
Let's make `curry` function that performs currying for binary functions. In other words, it translates `f(a, b)` into `f(a)(b)`:
314-
315-
```js run
316-
*!*
317-
function curry(func) {
318-
return function(a) {
319-
return function(b) {
320-
return func(a, b);
321-
};
322-
};
323-
}
324-
*/!*
325-
326-
// usage
327-
function sum(a, b) {
328-
return a + b;
329-
}
330-
331-
let carriedSum = curry(sum);
332-
333-
alert( carriedSum(1)(2) ); // 3
334-
```
335-
336-
As you can see, the implementation is a series of wrappers.
337-
338-
- The result of `curry(func)` is a wrapper `function(a)`.
339-
- When it is called like `sum(1)`, the argument is saved in the Lexical Environment, and a new wrapper is returned `function(b)`.
340-
- Then `sum(1)(2)` finally calls `function(b)` providing `2`, and it passes the call to the original multi-argument `sum`.
341-
342-
More advanced implementations of currying like [_.curry](https://lodash.com/docs#curry) from lodash library do something more sophisticated. They return a wrapper that allows a function to be called normally when all arguments are supplied *or* returns a partial otherwise.
343-
344-
```js
345-
function curry(f) {
346-
return function(..args) {
347-
// if args.length == f.length (as many arguments as f has),
348-
// then pass the call to f
349-
// otherwise return a partial function that fixes args as first arguments
350-
};
351-
}
352-
```
353-
354-
### Currying? What for?
355-
356-
Advanced currying allows both to keep the function callable normally and to get partials easily. To understand the benefits we definitely need a worthy real-life example.
357-
358-
For instance, we have the logging function `log(date, importance, message)` that formats and output the information. In real projects such functions also have many other useful features like: sending it over the network or filtering:
359-
360-
```js
361-
function log(date, importance, message) {
362-
alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
363-
}
364-
```
365-
366-
Let's curry it!
367-
368-
```js
369-
log = _.curry(log);
370-
```
371-
372-
After that `log` still works the normal way:
373-
374-
```js
375-
log(new Date(), "DEBUG", "some debug");
376-
```
377-
378-
...But also can be called in the curried form:
379-
380-
```js
381-
log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)
382-
```
383-
384-
Let's get a convenience function for today's logs:
385-
386-
```js
387-
// todayLog will be the partial of log with fixed first argument
388-
let todayLog = log(new Date());
389-
390-
// use it
391-
todayLog("INFO", "message"); // [HH:mm] INFO message
392-
```
393-
394-
And now a convenience function for today's debug messages:
395-
396-
```js
397-
let todayDebug = todayLog("DEBUG");
398-
399-
todayDebug("message"); // [HH:mm] DEBUG message
400-
```
401-
402-
So:
403-
1. We didn't loose anything after currying: `log` is still callable normally.
404-
2. We were able to generate partial functions that are convenient in many cases.
405-
406-
### Advanced curry implementation
407-
408-
In case you're interested, here's the "advanced" curry implementation that we could use above.
409-
410-
```js run
411-
function curry(func) {
412-
413-
return function curried(...args) {
414-
if (args.length >= func.length) {
415-
return func.apply(this, args);
416-
} else {
417-
return function(...args2) {
418-
return curried.apply(this, args.concat(args2));
419-
}
420-
}
421-
};
422-
423-
}
424-
425-
function sum(a, b, c) {
426-
return a + b + c;
427-
}
428-
429-
let curriedSum = curry(sum);
430-
431-
// still callable normally
432-
alert( curried(1, 2, 3) ); // 6
433-
434-
// get the partial with curried(1) and call it with 2 other arguments
435-
alert( curried(1)(2,3) ); // 6
436-
437-
// full curried form
438-
alert( curried(1)(2)(3) ); // 6
439-
```
440-
441-
The new `curry` may look complicated, but it's actually pretty easy to understand.
442-
443-
The result of `curry(func)` is the wrapper `curried` that looks like this:
444-
445-
```js
446-
// func is the function to transform
447-
function curried(...args) {
448-
if (args.length >= func.length) { // (1)
449-
return func.apply(this, args);
450-
} else {
451-
return function pass(...args2) { // (2)
452-
return curried.apply(this, args.concat(args2));
453-
}
454-
}
455-
};
456-
```
457-
458-
When we run it, there are two branches:
459-
460-
1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it.
461-
2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result.
462-
463-
For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`.
464-
465-
For the call `curried(1)(2)(3)`:
466-
467-
1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`.
468-
2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together.
469-
470-
As the argument count is still less than 3, `curry` returns `pass`.
471-
3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function.
472-
473-
If that's still not obvious, just trace the calls sequence in your mind or on the paper.
474-
475-
```smart header="Fixed-length functions only"
476-
The currying requires the function to have a known fixed number of arguments.
477-
```
478-
479-
```smart header="A little more than currying"
480-
By definition, currying should convert `sum(a, b, c)` into `sum(a)(b)(c)`.
481-
482-
But most implementations of currying in JavaScript are advanced, as described: they also keep the function callable in the multi-argument variant.
483-
```
484-
485201
## Summary
486202
487-
- Method `func.bind(context, ...args)` returns a "bound variant" of function `func` that fixes the context `this` and first arguments if given.
488-
489-
We may need to fix a context for passing an object method somewhere to be called. For example, to `setTimeout`.
490-
491-
- When we fix some arguments of an existing function, the resulting (less universal) function is called *a partial*. We saw other ways of making partials than `bind`.
492-
493-
Partials are convenient when we don't want to repeat the same argument over and over again. Like if we have a `send(from, to)` function, and `from` should always be the same for our task, we can get a partial and go on with it.
494-
495-
- *Currying* is a transform that makes `f(a,b,c)` callable as `f(a)(b)(c)`. JavaScript implementations usually both keep the function callable normally and return the partial if arguments count is not enough.
203+
Method `func.bind(context, ...args)` returns a "bound variant" of function `func` that fixes the context `this` and first arguments if given.
496204
497-
Currying is great when we want easy partials. As we've seen in the logging example: the universal function `log(date, importance, message)` after currying gives us partials when called with one argument like `log(date)` or two arguments `log(date, importance)`.
205+
Usually we apply `bind` to fix `this` in an object method, so that we can pass it somewhere. For example, to `setTimeout`. There are more reasons to `bind` in the modern development, we'll meet them later.

0 commit comments

Comments
 (0)