What exactly does this do? [duplicate] - javascript

So I get that an array of [200,599] is returned from the promise and the callback function inside spread is being passed into Function.apply.bind, but now I'm lost. How is the array of [200,599] split into x and y? How exactly does the apply.bind work?
function getY(x) {
return new Promise( function(resolve,reject){
setTimeout( function(){
resolve( (3 * x) - 1 );
}, 100 );
} );
}
function foo(bar,baz) {
var x = bar * baz;
// return both promises
return [
Promise.resolve( x ),
getY( x )
];
}
function spread(fn) {
return Function.apply.bind( fn, null );
}
Promise.all(
foo( 10, 20 )
)
.then(
spread( function(x,y){
console.log( x, y ); // 200 599
} )
)

.apply() is a method on function objects. Like so:
console.log(Math.max.apply(null, [1, 2, 3])); // 3
.apply() accepts two arguments, the context (what would become this inside of the target function) and an iterable of arguments (usually an array, but the arguments array like also works).
.bind() is a method on function objects. Like so:
const x = {
foo: function() {
console.log(this.x);
},
x: 42
}
var myFoo = x.foo.bind({x: 5});
x.foo(); // 42
myFoo(); // 5
.bind() takes a context (what would become this), and optionally, additional arguments, and returns a new function, with the context bound, and the additional arguments locked
Since .apply() is a function in on itself, it can be bound with .bind(), like so:
Function.prototype.apply.bind(fn, null);
Meaning that the this of .apply() would be fn and the first argument to .apply() would be null. Meaning that it would look like this:
fn.apply(null, args)
Which would spread the parameters from an array.

Spread takes a function and binds the apply method to, partially applying the null argument. So in short,
spread(fn)
is transformed to
args => fn.apply(null, args)
which is the same as using the ES6 spread syntax
args => fn(...args)
where the function got its name from.
If you want the long answer, remember what bind does:
method.bind(context, ...args1)
returns a function that works like
(...args2) => method.call(context, ...args1, ...args2)
In our case, method is apply, the context is fn and the first arguments are null, so the call
Function.apply.bind( fn, null )
will create a function that works like
(...args2) => (Function.apply).call(fn, null, ...args2)
which is equivalent to the fn.apply(…) call above, given that apply is the method inherited from Function.prototype in both accesses.

The spread function is just a utility function to convert an array, into parameters passed to a function. The apply is doing the converting, and the bind is binding it to the calling function so that the "this" context is connected to same function.
To see how spread is working in a simpler form ->
spread(function (x,y){console.log(x,y);})([1,2])
You will get the answer, 1 2, as 1 is passed to x, and 2 is passed to y.
So in your example your promise.all is returning an array of resolved promises.
These are then getting mapped to parameters to your function(x,y).

The reason it works is the "destructuring" nature of apply (if given an array of values, they would be provided spreaded to the function you use apply on).
Now back to your code when calling bind on apply, the value returned is a function which returns the same function provided to bind, the only thing different is when executed it would be called using apply (with an array as thisArg in your case), but it isn't going to be executed until you call it.
In your case when the promise has resolved, the function provided tothen woule be executed with an array of arguments provided by Promise resolution.
function spread(fn){
let boundedFn = fn.bind(fn)
return function(arg){
return boundedFn.apply(null,arg)
}
}
spread((x, y, c) => console.log(x, y, c))([1,2,3])
// or
Promise.resolve([6, 5]).then(spread((a, b) => console.log(a, b)))
The reason bind is provided (in your code) with null as second param is to prevent the array provided by the caller from being given to apply as its first param, which reserved for this.

Related

Why using bind is necessary in this implementation of curry function

There is a curry function from https://www.30secondsofcode.org/js/s/curry.
const curry = (fn, arity = fn.length, ...args) => {
if (args.length >= arity) {
return fn(...args)
} else {
return curry.bind(null, fn, arity, ...args) // line(*)
}
}
console.log(curry(Math.pow)(2)(10)) // 1024
console.log(curry(Math.min, 3)(10)(50)(2)) // 2
Why using bind is necessary in line(*)? Can it be replaced with
return curry(fn, arity, ...args) // line(**)
?
There are only two reasons to ever use Function#bind:
When you want to bind the function to a particular context object. Practically speaking, when you want to make sure ahead of time that the this keyword inside the function will refer to a particular, known object.
When you want to pre-define some arguments, a.k.a. "partial function application".
The second case lends itself perfectly to what function currying is all about - making an N-ary function flexibly callable like:
fn(arg1, arg2, ..., argN), or
fn(arg1, arg2, ...)(argN), or
fn(arg1, arg2)(..., argN), or
fn(arg1)(arg2)(...)(argN).
The important thing to notice here is that we need multiple separate functions for all cases but the first. Let's say you have a worker function that takes 3 arguments. You can...
...pass enough arguments for the worker function, i.e. 3 or more. Then curry() calls the worker function with those 3 arguments and returns the result.
This makes fn(arg1, arg2, arg3) possible.
...pass too few arguments for the worker function. Then curry() does not call the worker, but must return a new function which takes the remaining number of arguments.
This makes all of fn(arg1)(arg2, arg3) and fn(arg1, arg2)(arg3) and fn(arg1)(arg2)(arg3) possible.
Function#bind covers the second case: It creates a new wrapper function for the worker and pre-fills some of the worker's argument slots with the given values. The context object is irrelevant for that intention, so using null here is fine.
const curry = (fn, arity = fn.length, ...args) =>
// enough arguments ? call : remember arguments so far & defer
args.length >= arity ? fn(...args) : curry.bind(null, fn, arity, ...args);
So to answer the question: No, you can't use return curry(fn, arity, ...args) because this does not do the most important thing, which is to create a new function.
Example: Let's say we have a worker function that searches a string and returns the number of hits.
const search = (str, substr) => {
var hits = 0, pos = 0;
while (true) {
pos = str.indexOf(substr, pos) + 1;
if (pos) hits++; else return hits;
}
}
Now we could create a curried version that remembers the target string for us and we only need to switch the last argument:
const flexibleSearch = curry(search);
const reusableSearch = flexibleSearch("... a ... b ... c ... a ... b ... a");
reusableSearch("a") // -> 3
reusableSearch("b") // -> 2
reusableSearch("c") // -> 1
reusableSearch("d") // -> 0
#Tomalak is correct
Lets say you have a function that takes 100 parameters fn(1,2,3,...100) {}.
fn.bind(null, 99, 100) creates a new function with preset leading argument. In our example will use the default values for parameters 1,2,...97,98 and expect two parameters as input for the new function that will be used as parameter 99, and 100.
In this case, we are currying a function to accept partial arguments.
You can go through these to get a deeper understanding
Wiki for currying and
MDN docs for binding partial functions

JavaScript: Passing different count of parameters to callback function [duplicate]

Can I call a function with array of arguments in a convenient way in JavaScript?
Example:
var fn = function() {
console.log(arguments);
}
var args = [1,2,3];
fn(args);
I need arguments to be [1,2,3], just like my array.
Since the introduction of ES6, you can sue the spread syntax in the function call:
const args = [1,2,3];
fn(...args);
function fn() {
console.log(arguments);
}
Before ES6, you needed to use apply.
var args = [1,2,3];
fn.apply(null, args);
function fn() {
console.log(arguments);
}
Both will produce the equivalent function call:
fn(1,2,3);
Notice that I used null as the first argument of the apply example, which will set the this keyword to the global object (window) inside fn or undefined under strict mode.
Also, you should know that the arguments object is not an array, it's an array-like object, that contains numeric indexes corresponding to the arguments that were used to call your function, a length property that gives you the number of arguments used.
In ES6, if you want to access a variable number of arguments as an array, you can also use the rest syntax in the function parameter list:
function fn(...args) {
args.forEach(arg => console.log(arg))
}
fn(1,2,3)
Before ES6, if you wanted to make an array from your arguments object, you commonly used the Array.prototype.slice method.
function fn() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
}
fn(1,2,3);
Edit: In response to your comment, yes, you could use the shift method and set its returned value as the context (the this keyword) on your function:
fn.apply(args.shift(), args);
But remember that shift will remove the first element from the original array, and your function will be called without that first argument.
If you still need to call your function with all your other arguments, you can:
fn.apply(args[0], args);
And if you don't want to change the context, you could extract the first argument inside your function:
function fn(firstArg, ...args) {
console.log(args, firstArg);
}
fn(1, 2, 3, 4)
In ES5, that would be a little more verbose.
function fn() {
var args = Array.prototype.slice.call(arguments),
firstArg = args.shift();
console.log(args, firstArg);
}
fn(1, 2, 3, 4);
In ECMAScript 6, you can use spread syntax (...) for that purpose. It's way simpler and easier to understand than Function.prototype.apply().
Code example:
const fn = function() {
console.log(arguments);
}
const args = [1,2,3];
fn(...args);

Understanding trampoline optimisation for recursive functions

I have got the following trampoline implementation:
function trampoline(f) {
while (f && f instanceof Function) {
f = f.apply(f.context, f.args);
}
return f;
}
And it works likewise, example being factorial:
function factorial(n) {
function recur(n, acc) {
if (n == 0) {
return acc;
} else {
return recur.bind(null, n-1, n*acc);
}
}
return trampoline(recur.bind(null, n, 1));
}
The problem is that I don't understand how f.context and f.args are passed in as arguments, those are properties that clearly can't be found on recurred function inside the trampoline when i, for instance, try to access and console.log them. It then logs undefined values.
What is the mechanism of this particular implementation that is passing arguments of the function?
As you can see here, function objects do not have properties called args and context, and that's why they appear as undefined in the console log. Inside the function itself, you can access these values in the arguments and this objects, but they are not visible from the outside in bound functions (or unbound functions).
The reason your code works is because the apply parameters are appended if the function is bound with the parameters. When you bind a function, you can bind only the context (this) or the context and the parameters. If you bind the context and the parameters, when you apply or call that function, the parameters you pass in will be appended at the end of the parameters list. In your particular case, you are doing...
f = f.apply(undefined, undefined);
... and the resulting parameters for f would be:
[...parametersWhenTheFunctionWasBound, undefined, undefined]

Functional Programming - .bind.apply for curry function

Reading about functional programming - got to currying, example has a simple currying function. I understand everything except the last else block.
var curry = function (fn, fnLength) {
fnLength = fnLength || fn.length;
return function () {
var suppliedArgs = Array.prototype.slice.call(arguments);
if (suppliedArgs.length >= fn.length) {
return fn.apply(this, suppliedArgs);
} else if (!suppliedArgs.length) {
return fn;
} else {
return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
}
};
};
If the supplied args are >=, call the function with the supplied arguments.
Else if suppliedArgs.length is falsy, return the original function without doing anything.
Else ???
I think recursively call the function?
I don't understand what .bind.apply accomplishes.
Is [this] just in an array because suppliedArgs.push wouldn't return the array?
Start by looking at how you call Function#bind():
fun.bind(thisArg[, arg1[, arg2[, ...]]])
Then consider how you use Function#apply():
fun.apply(thisArg, [argsArray])
So given for bind() we need to call it on a function, and give it multiple parameters (not an array), and all we have is an array of arguments (suppliedArgs in the code), then how can we do that? Well, the main way you can call a function that takes multiple arguments instead of a single argument that is an array is to use .apply() on the function. So then we have fn.bind.apply(...something...).
The first parameter to .apply() is the this value - which needs to be the function to be bound in the case of .bind() (see below for an explanation of why). Hence fn.bind.apply(fn, ...).
Then, the second parameter to .apply() is an array of all the arguments to the function you are invoking, which in the case of .bind() is thisArg[, arg1[, arg2[, ...]]]. Hence we need a single array, with the first value being the value for this within the function, followed by the other arguments. Which is what [this].concat(suppliedArgs) produces.
So the whole fn.apply.bind(fn, [this].concat(suppliedArgs)) thing produces a correctly bound function that will have the supplied arguments to the current function "prefilled", with the correct this context. This function that is produced is then passed as the fn parameter in a recursive call to curry(), which in turn will produce another function in the same way as the top level call will.
The overall effect is that whenever you call a function created by curry(), if you don't pass the expected number of parameters, you will get a new function which takes the remaining number of parameters, or you will evaluate the original function with the entire list of parameters passed in correctly.
e.g.
function addAllNums(a, b, c, d, e) {
return a + b + c + d + e;
}
var curriedAddAll = curry(addAllNums, 5);
var f1 = curriedAddAll(1); // produces a function expecting 4 arguments
var f2 = f1(2, 3); // f2 is a function that expects 2 arguments
var f3 = f2(4); // f3 is a function that expects 1 argument
var f4 = f3(5); // f4 doesn't expect any arguments
var ans = f4(); // ans = 1 + 2 + 3 + 4 + 5 = 15.
// OR var ans = f3(5); => same result
Why the different thisArg values?
Probably the most confusing thing about this line of code is the two different values for thisArg in .bind() and .apply().
For .apply(), the thisArg is what you want the value of this to be inside the function you are calling .apply() on. e.g. myFunction.apply(myObj, ['param1', 'param2']) is equivalent to myObj.myFunction('param1', 'param2').
In this particular case, .bind() is executed on the fn function, so we want fn to be the this value for .bind(), so it knows what function it is creating a bound version of.
For .bind(), the thisArg is what the value of this will be inside the bound function that is returned.
In our case, we want to return a bound function that has the same this value as we currently have. In other words, we want to maintain the this value correctly within the new function, so it doesn't get lost as you create new functions which happens when you call a curried function with less arguments than it expects.
If we did not maintain the this value correctly, the following example wouldn't log the correct value of this. But by maintaining it, the correct value will be output.
var myObj = {
a: 1,
b: curry(function (a, b, c, d) {
console.log('this = ', this);
return a + b + c + d;
})
};
var c = myObj.b(1,1,1); // c is a function expecting 1 argument
c(1); // returns 4, and correctly logs "this = Object {a: 1, b: function}"
// if "this" wasn't maintained, it would log the value of "this" as
// the global window object.
The last else block is the main and most important part of the curry function, as it is the actual line that carries the logic for currying.
return curry(fn.bind.apply(fn, [this].concat(suppliedArgs)), fnLength - suppliedArgs.length);
This is what returns the new function that needs n-1 arguments from your previous function. Why? It's a combination of multiple things:
fn.bind.apply simply calls a function in the context of the function itself, while supplying the needed args (suppliedArgs). Note how the next parameter to curry is fnLength - suppliedArgs.length, which reduces the arguments needed to what was passed.
Let's explain it with the help of ES6. Things are going to become more obvious.
// Imagine we have the following code written in ES5
function fn(a, b, c) {
console.log(a, b, c);
}
var arr = [1, 2, 3];
var funcWithBoundArguments = fn.bind.apply(fn, [null].concat(arr));
Let's convert ES5 to ES6 code
// ES6
function fn(a, b, c) { console.log(a, b, c) }
let arr = [1,2,3];
let funcWithBoundArguments = fn.bind(null, ...arr)
You see? When you bind a function we have to explicitly enumerate all the arguments like:
fn.bind(null, 1, 2, 3)
But how could we bind the content of an array if we don't know its content in advance?
Right, we have to use .bind.apply() where:
the 1st argument of apply is the function (fn) we bind
the 2nd argument is an array which gets the context (as the first item of array) that we bind our function to and the rest of the items of the array are the arguments (which number is variable) we bind to our function (fn).

Javascript: Forwarding function calls that take variable number of arguments [duplicate]

This question already has answers here:
Passing arguments forward to another javascript function
(5 answers)
Closed 5 years ago.
I think I need something like ruby's splat * here.
function foo() {
var result = '';
for (var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
return result;
}
function bar() {
return foo(arguments) // this line doesn't work as I expect
}
bar(1, 2, 3);
I want this to return "123", but instead I get "[object Arguments]". Which makes sense, I suppose. It's passing the object that represents the arguments, but not the arguments individually.
So how do I simply forward any number of arguments to another function that takes any number of arguments?
UPDATE: Since ES6, you can use the spread syntax to call a function, applying the elements of an iterable object as argument values of the function call:
function bar() {
return foo(...arguments);
}
Note that you can also receive a variable number of arguments as a real array, instead of using the arguments object.
For example:
function sum(...args) { // args is an array
return args.reduce((total, num) => total + num)
}
function bar(...args) {
return sum(...args) // this just forwards the call spreading the argument values
}
console.log(bar(1, 2, 3)); // 6
In the days of ES3/ES5, to correctly pass the arguments to another function, you needed to use apply:
function bar() {
return foo.apply(null, arguments);
}
The apply method takes two parameters, the first is the thisObj, the value of it will be used as the this value inside the invoked function, if you use null or undefined, the this value inside the function will refer to the global object, in non-strict mode, otherwise is undefined.
The second argument that apply expects is an array-like object that contains the argument values to be applied to the function.
Check the above example here.
Try this return foo.apply(this,arguments). Also you can just use Array.prototype.slice.apply(arguments).join('') for your foo function.

Categories

Resources