I have a simple function that takes one argument
fn = function(argument) {console.log(argument)}
In setInterval, I want to call the function and pass an external variable:
argument = 1
setInterval(<MY FUNCTION WITH ACCESS TO ARGUMENT>, 1000)
I realize that I could do it with a higher-order function, i.e.
fn = function(argument) {
function () {
console.log(argument)
}
}
argument = 1
setInterval(fn(argument), 1000)
And this does work, but I want to know if it can be done with curry.
I've tried:
fn = _.curry(fn)("foo")
// since the function takes only one argument,
// here it is invoked and no longer can be
// passed as a function to setInterval
fn = _.curry(fn, 2)("foo")
// setting the arity to 2 makes it so the function
// isn't invoked. But setInterval does not pass
// the additional argument and so it never ends
// up getting invoked.
I feel like there is something I'm missing with these curry examples. Am I, or will curry not help here?
Indeed lodash _.curry seems not suitable for your use-case.
But you can use the vanilla JavaScript bind for this:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])
In your case your code would look like this:
fn = function(argument) {console.log(argument)}
var argument = 1
setInterval(fn.bind(this, argument), 1000)
Lodash alternative
If you really want to do it the lodash way, the equivalant of fn.bind(thisArg, ...args) is _.bind(fn, thisArg, ...args). And if you are not interested in setting the this reference, then you can save one argument with _.partial(fn, ...args):
Creates a function that invokes func with partials prepended to the arguments it receives. This method is like _.bind except it does not alter the this binding.
Related
say I have this simple situation:
const f = function(){
const fn = f.bind(null, arguments);
}
I am trying to implement a "retry" mechanism - this code checks out according to the static analysis and seems to the signature of Function.prototype.bind, but my question is:
is arguments going to be applied as simply the first argument to f or will it be spread out, as in, f.apply(null, arguments)? I am having trouble finding an example of this online.
.bind works similar to .call, not .apply - the second argument will be treated as just that, the second argument. So in your example:
f(1,2,3) would produce fn([1,2,3])
arguments will be passed as simply the first argument to f. Moreover, if you then call the bound function with more arguments, those arguments will come after the bound one (i.e. you cannot overwrite that first argument).
read more here
Yeah, so Function.prototype.bind has similar signature to Function.prototype.call
so you need to do:
fn.bind(null, ...arguments);
or
fn.bind(null, ...Array.from(arguments));
I have the function
function bind(method, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
var a = args.concat(Array.prototype.slice.call(arguments, 0));
return method.apply(context, a);
}
}
The question was: how this function working and for what it can be used.
I understand that function bind (and return function too) convert array-like arguments into a real array. Method and context is not converted into array (because of 2 index). I can pass extra args in bind function and args into returned function and call method with context as 'this'.
My question is - how it can be used, in what cases. Is method and context - function or objects, or function and object?
This function is usual to produce an event handler function.
When you subscribe to your function to an event, for example, and the function is called the this depends on which call your handler.
If you pass a method of your object instead a function, you want to access your object by this.
The function first 2 arguments are the method (the function you want to bind), the context (the object you want to access on this), and you could add some other fixed arguments, that will be pass to your handler function each time is called.
Then the function return a new function, that you use to subscribe the event listener you need, and this function get all the argument passed by this event and add to the arguments array.
And finally do the magic with apply that allow you to call a function changing the context and passing an arbitrary array as the arguments of the function.
You could use this kind of function not only in event subscribtion, but even in call like array forEach, map and so on.
My html contains two forms overlapping each other, one used as add form and one as edit form. I use jQuery to show and hide them with the following code:
var editForm = $("#edit-form");
var addForm = $("#add-form");
var showEditForm = function() {
editForm.fadeIn(function() {
addForm.fadeOut();
});
};
var showAddForm = function() {
editForm.fadeOut(function() {
addForm.fadeIn();
});
};
I wanted to make the code more compact so I set the fadeOut() call directly on the fadeOut() callback by doing like this:
var showEditForm = function() {
editForm.fadeIn(addForm.fadeOut);
};
var showAddForm = function() {
editForm.fadeOut(addForm.fadeIn);
};
But this productes the following error Uncaught TypeError: Failed to execute 'animate' on 'Element': Valid arities are: [1], but 4 arguments provided. but why doesn't that work?
That's because calling a function as a property of an object is a special syntax, that calls the function with the object as context.
When you call a function like this:
obj.func();
then this will be a reference to obj inside the function.
If you get the reference to the function, and then call it:
var f = obj.func;
f();
then this will be a reference to the global context, i.e. the window object.
By using editForm.fadeIn(addForm.fadeOut); you get the reference to addForm.fadeOut and send to the fadeIn method. It's no longer associated with the object, so it will be called with the global context instead of the object as context.
You can use the proxy method to associate the function with the object, so that it will be called with the correct context:
var showEditForm = function() {
editForm.fadeIn($.proxy(addForm.fadeOut, addForm));
};
var showAddForm = function() {
editForm.fadeOut($.proxy(addForm.fadeIn, addForm));
};
I suspect the problem is that addForm.fadeOut is being called with a bad combination of arguments, when its passed to the fadeIn function (and vice versa).
The classic example of this pitfall seems to be:
["0", "1", "2", "3"].map(function(i) {return parseInt(i);})
This, works as expected and gives [1,2,3,4] as a result. You might expect that you could shorten this, much as you did above, and write
["0", "1", "2", "3"].map(parseInt);
Unfortunately; this evaluates to [0, NaN, NaN, NaN]. The problem, is that .map calls any function provided it with three arguments: the value, the index, and the array itself, and parseInt takes up to two arguments: the value, but also the radix/base to parse in. (e.g. radix 2 to parse a string as binary) So what actually happens is essentially:
[
parseInt("0", 0), //0, radix is ignored
parseInt("1", 1), //NaN, what is base 1?
parseInt("2", 2), //NaN, 2 isn't valid binary
parseInt("3", 3) //NaN, 3 isn't valid ternary/base-3
]
I suspect, based on the error message, that the same thing is going on here. The "arity" of a function is the number of arguments passed to it, so the error message here says that 4 arguments were provided, when only one was expected.
In general with functions that take optional arguments, you need to be careful before passing them to other functions directly, or else you can't control what arguments it will be called with.
Fiddle: http://jsfiddle.net/jmj8tLfm/
addForm.fadeIn and addForm.fadeOut are being called without specifying the this context that would normally be passed when you call addForm.fadeIn(). Try .bind()-ing the this variable appropriately as follows:
var showEditForm = function() {
editForm.fadeIn(addForm.fadeOut.bind(addForm));
};
var showAddForm = function() {
editForm.fadeOut(addForm.fadeIn.bind(addForm));
};
If you are writing in vanilla js. The reason as to why you need too pass the callback function in an anonymous function has to do with function invocation.
Take a look at this example:
const firstFunc = (callback) => {
setTimeout(function() {
console.log('yes');
console.log(callback())
}, 3000);
}
const secondFunc = () => console.log('great');
firstFunc(function(){
secondFunc();
});
// prints 'yes' 'great' after 3 seconds
> yes
great
When invoking the function, if you pass the callback argument without the parenthesis i.e firstFunc(secondFunc); the callback function will invoke after the first function has finished (just like above) provided inside the first function where the callback gets called is invoking that function. i.e callback(), the () is the important part.
Try omitting the parenthesis inside the first function like this, callback and pass the second function as a callback without the parenthesis firstFunction(secondFunction) notice how you are passing the callback function but it is never being invoked. Your console.log() should look like this.
> yes
() => console.log('great')
So why does this matter...
If you pass the function invocation as firstFunc(secondFunc()) using the setup from the first code snippet. You will notice that the second function prints first then 3 seconds later the first function is invoked.
> great
yes
Given that Javascript is event driven, the invocation of the second function can be found const secondFunc = () => console.log('great'); and it will immediately invoke that function not waiting for the response from the first function. That is what the () did when you invoked secondFunc() inside firstFunc.
By passing an anonymous function that function is never being invoked until it reaches the callback() invocation part. Again using the setup from the first code snippet, try.
firstFunc(function(){
secondFunc();
});
firstFunc(function(){
secondFunc();
}())
See how the second call invokes the secondFunc right away. What is happening is the anonymous function is a wrapper to not invoke your function right away.
Why is this useful?
If secondFunc takes a callback that callback function would not be invoked until the second function has finished executing. You would need to call that callback function inside your second function.
firstFunc(function(){
secondFunc(function(){
thirdFunc();
});
});
The following piece of code (from msdn) is a simple implementation of the 'bind' function:
/* Approximation of `Function.prototype.bind` from ES5 (without error checking) */
Function.prototype.bind = function(thisArg) {
var fn = this, args = *Array.prototype.slice.call(arguments, 1)*;
return function() {
return fn.apply(thisArg, args.concat(*Array.prototype.slice.call(arguments, 0)*));
};
};
Can anyone explain the first call to Array.prototype.slice.call ? I understand that arguments is not an array and one needs to turn it into an array before using slice and concat. I don't understand the first call - aren't we losing the first element when calling
Array.prototype.slice.call(arguments, 1)?
You are correct.
The zeroth element of arguments is thisArg, which is why it is getting removed.
According to the docs about bind, the first argument (arguments[0]) is the custom this value to be used as the value of this within the the function returned by bind (the "bound function").
What follows (arguments[1] - arguments[n]) are arguments that are to be prepended when calling the bound function, in addition to the arguments that are provided to when called.
What the first Array.prototype.slice.call does is to slice the arguments passed to bind call and get the arguments to be prepended starting from the second argument passed, leaving behind the first argument which would be our this.
For example
var newFN = someFunction.bind(myNewThis,foo,bar,baz);
The first Array.prototype.slice.call takes foo, bar and baz.
In the returned function, foo, bar and baz get prepended to the arguments provided when calling the bound function:
//fn - original function
//args - extracted arguments foo, bar and baz
//thisArg - the provided `this` value, myNewThis
//this code basically:
// - calls the original function (fn)
// - provides a custom `this` value (thisArg)
// - provides arguments that comprise the extracted arguments + the passed arguments
fn.apply(thisArg, args.concat(Array.prototype.slice.call(arguments, 0)));
So when you use the new "bound" function, you get a custom this value, as well as a "preset", prepended arguments list:
newFN('ban','bam'); //arguments === ['foo','bar','baz','ban','bam'];
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
**args.concat(Array.prototype.slice.call(arguments))**);
};
};
This function is in Prototype. Does it equal to:
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,**args**);
};
};
In my opinion, args.concat(Array.prototype.slice.call(arguments)) == args, since the anonymous
function haven't any arguments. What is the matter?
No, they aren't the same.
Tthe purpose of concatenating the arguments is to provide a way of partially apply (or curry) the function, pre-filling arguments when bind is used and being able to add more later when the function that bind returns is used, for example:
var obj = {
fx: function() {
alert(Array.prototype.join.call(arguments, ', '));
}
};
var fx2 = obj.fx.bind(obj, 1, 2, 3);
fx2(4, 5); // Alerts "1, 2, 3, 4, 5"
As you can see in the last two lines of code, when I declare fx2, I'm passing obj as the first argument (this will ensure the context, used as the object variable on the bind implementation), then I pass the values 1,2 and 3.
Those values are stored in the args variable of the outer closure of bind, then as you see in the bind implementation, another function is returned.
That function returned in my example is fx2 after the assignment, in the last line you see I call that function, passing two additionally arguments.
Finally the returned function will call obj.fx with the two argument lists, the arguments we pre-filled when calling bind (1,2,3) and the arguments when the function actually executed (4,5).
That is why makes sense concatenating the two argument objects.
the anonymous function haven't any arguments
The anonymous function can actually have arguments (as can the bind method itself, which also doesn't declare any arguments).
Type-checking in JavaScript is non-existent: you can pass fewer arguments into a function than are declared in the function(...) signature (in which case the arguments that aren't passed get received as undefined), and you can pass more than declared, in which case the only way to read them is through the arguments array, which always contains exactly how many arguments were passed in, regardless of what's in the function signature.
It's generally considered polite to put a comment in the signature (function(x, /* y, ... */)) to indicate that more arguments will be read using the arguments array.