Javascript dynamic callback functions - javascript

I am trying to design a function that will accept different functions as argument (callbacks). These functions can have different number of arguments. Is there a way I can execute the callback directly without bothering the number of arguments
For example :
function displayDialog(callBack)
{
callBack();
};
This function can be executed as follwing:
displayDialog(myFunction);
Where myFunction is
function myFunction(){}
or function `myFunction(a,b){}

Thank you all, for your answers. I tried using the apply and it worked. Here is what I did:
function caller()
{
var args = ['a', 'b'];
displayDialog(myFunction, args);
}
and in displayDialog function:
function displayDialog(callBack, args)
{
callBack.apply(null, args);
};

Normally, the arguments to a callback are dictated by the code that calls it. That is, your displayDialog function would document what arguments it gives to the callback.
If someone using displayDialog needs information in their callback that displayDialog doesn't provide, that's the caller's problem. They have a couple of different things they can do:
Make their callback close over the information they need
Use Function#bind or similar to curry the arguments into their function
Here's an example of #1:
// Documentation: The callback will be called with the
// number 42
function displayDialog(callback) {
setTimeout(function() {
callback(42); //
}, 35);
}
// A function using displayDialog
function useIt() {
var str = "I'm some information the callback needs";
displayDialog(function(arg) {
snippet.log("arg = " + arg);
snippet.log("str = " + str);
});
}
snippet.log("Calling useIt");
useIt();
snippet.log("useIt completed");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Here's an example of #2:
// Documentation: The callback will be called with the
// number 42
function displayDialog(callback) {
setTimeout(function() {
callback(42); //
}, 35);
}
// A function using displayDialog
function useIt() {
displayDialog(function(arg0, arg1) {
snippet.log("arg0 = " + arg0);
snippet.log("arg1 = " + arg1);
}.bind(null, "I'm a curried argument"));
}
snippet.log("Calling useIt");
useIt();
snippet.log("useIt completed");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

You can call a function, that have args set in the declaration, without supplying any arguments. The parameters will be undefined in the function though, so do a check if they are actually set before using them.
Example:
function callbackFunction(arg1, arg2) {
// First call, arg1 will be "a" and arg2 will be undefined
// Second call, arg1 and arg2 will be undefined
// Third call, arg1 will be "a" and arg2 will be "b"
if(arg1 !== undefined) {
// You can use arg1.
}
if(arg2 !== undefined) {
// You can use arg2
}
// Easiest way to handle this is to set it to a default value if its not set:
// (usually done at the top of the function).
arg1 = arg1 === undefined ? "default" : arg1;
arg2 = arg2 === undefined ? "default" : arg2;
}
callbackFunction("a");
callbackFunction();
callbackFunction("a", "b");
You can also skip using arguments in the function declaration and then use the argument list:
function callbackFunction() {
for(var i=0;i<arguments.length;i++) {
console.log(arguments[i]);
}
}

Related

No binding of arguments in JavaScript

Can anyone please explain me the behavior of this code?
var arguments = 42;
var arr = () => arguments;
arr(); // 42
function foo() {
var f = (i) => arguments[0] + i; // foo's implicit arguments binding
return f(2);
}
foo(1); // 3
I know what implicit arguments binding is.
What I don't understand is how foo(1) is returning 3?
What does return f(2) does? As in which function it calls?
PS: I'm following this Mozilla docs.
Arrow functions do not bind arguments so when you use arguments[0] inside f you are accessing foo's arguments (ie 1). Since you have passed 2 as i you get 1 + 2
For example if you use an arrow function that tries to access arguments outside of a function call you should get a ReferenceError
const f = i => console.log(arguments[0])
try {
f()
console.assert(false, 'should not get here')
} catch (e) {
console.assert(e instanceof ReferenceError,
'should get reference error on trying to access arguments')
console.log(e.message)
}
To make this a little clearer for you I'll re-write it as ES5
function foo(){
var arg1 = arguments[0]; // argument is 1 in your example
var f = function(i) {
return arg1 + i; // i is 2 in your example
};
return f(2); // returns 3
}
foo(1); // sets argument to 1 and returns 3
In ES6 arrow functions do not have an arguments property like standard functions. You're accessing the foo function arguments array like object. As the first argument you passed in is 1, the arrow function accesses the foo function arguments object and retrieves the first value within the arrow function.

How to access callback arguments within another function?

I am trying to capture the arguments that are being passed to callback 'func' function. However, when I try to console log arguments inside my 'cache' function it doesn't give anything except the name of the callback function.
But when I add a secondary inner function, the logging works just fine and it lets me access the arguments received by the callback. I really want to understand how inner function can perform the task, but the outer function cannot.
function cache(func) {
console.log(arguments); //logs { '0': [Function: complexFunction] }
return function () {
console.log(arguments); //logs { '0': 'foo', '1': 'bar' }
}
}
var complexFunction = function(arg1, arg2) { return arg1 + arg2 };
var cachedFunction = cache(complexFunction);
console.log(cachedFunction('foo', 'bar')); // complex function should be executed
The inner function is a different function. When you call it (because it has been returned and assigned to cachedFunction), you pass it different arguments.
cachedFunction('foo', 'bar')
complex function should be executed
It isn't.
You never execute complexFunction.
You pass it as an argument to cache, and cache passes it (written in the arguments object) to console.log, but it never gets called.
If you want to call it, then you need to actually do that.
function cache(func) {
console.log(arguments); //logs { '0': [Function: complexFunction] }
return function () {
console.log(func.apply(null, arguments));
}
}

ES6 default values not available in function.arguments?

If I have this ES6 function declaration and invocation:
function myFunction (arg1, arg2 = "bob") {
console.log("arguments", arguments);
}
myFunction(1);
...the console.log() statement shows only one argument with a value of "1". "bob" is nowhere to be seen. Is this expected and/or desired behavior? I would expect that default values would be available in the arguments object. If not, is there a way to dynamically get all arguments + defaults in some other manner?
Thanks in advance!
Yes, this is expected and desired. The arguments object is a list of the values that were passed into the function, nothing else.
It is not implicily linked to the parameter variables (that get assigned the default values), like it was in sloppy mode.
Is there a way to dynamically get all arguments + defaults in some other manner?
No. What parameters you have and whether they have default initialisers is static, you don't need to do anything here dynamically. You can do Object.assign([], arguments, [arg1, arg2]) for your example function.
As you know by now, there is no native method to get both "passed arguments AND defaults where arguments are not passed". But there is a workaround:
This function (that I found here) gets all parameters of a given function:
function getArgs(func) {
var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
return args.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
};
So, combining this function with the arguments of your function myFunction, we can get an array that has what you want:
function myFunction (arg1, arg2 = "bob") {
var thisArguments = arguments;
console.log(getArgs(myFunction, thisArguments));
};
function getArgs(func, argums) {
var args = func.toString().match(/function\s.*?\(([^)]*)\)/)[1];
var argsArray = args.split(',').map(function(arg) {
return arg.replace(/\/\*.*\*\//, '').trim();
}).filter(function(arg) {
return arg;
});
for(var i = 0; i < argsArray.length; i++){
argsArray[i] += " (default)";
}
var defaults = argsArray.slice(argums.length);
argums = Array.prototype.slice.call(argums);
return argums.concat(defaults);
};
Now, we can see the information in the console calling myFunction:
1. Passing more arguments than parameters
This will return only the arguments.
myFunction("foo", "bar", "baz");
//returns: ["foo", "bar", "baz"]
2. Passing less arguments than parameters
Will return the arguments and the remainder parameters as default, as you want (I added "default" to each string).
myFunction("foo");
//returns ["foo", "arg2 = "bob" (default)"]
3. Passing no arguments
This will return all the parameters.
myFunction();
//returns ["arg1 (default)", "arg2 = "bob" (default)"]
This is the fiddle: https://jsfiddle.net/gerardofurtado/25jxrkm8/1/

Slice JS function's arguments

I want to write a common confirm method like
var confirmDelete = function (fun) {
if (confirm("Do you want to delete " + arguments[1])) {
$(arguments[2]).remove();
fun(arguments[3]);
}
return false;
}
It's working fine for fun with one parameter, but I want to suit for two or more parameters, how I can do it?
every javascript Function object has a method named apply. apply will call your function using the given context and the given arguments.
var confirmDelete=function(fun) {
if(confirm("Do you want to delete "+ arguments[1])) {
// remove the first two elements in arguments, and use the resulting array as a new set of
// arguments to fun
fun.apply(this, Array.slice(arguments, 2));
}
}
In JavaScript you can pass as many parameters as you want.
If you don't pass them, they will be undefined.
So... You can do something like this:
var confirmDelete = function(arg1, arg2, arg3) {
if (typeof arg2 === 'undefined') {
arg2 = "default value for arg2";
}
if (typeof arg3 === 'undefined') {
arg3 = "default value for arg3";
}
// do more stuff...
}
You can also read about the magical arguments variable here.
You can rewrite your function something like
var confirmDelete = function fun(arg1, arg2) {
// number of parameter as your need
// TODO some stuff
fun(arg1 -1, arg2 +3); // call it recursively
return false;
}

Modifying callback function's argument(s) before calling them

I have a custom object that implements a function that'll be executed later. Here's how someone would call it:
customObject.onSomething(function(e) {
// do something with e
console.log('foobar');
});
Here's how onSomething is getting created:
var CustomObject = function() {
this.onSomething = function(callback) {
// If the user passes in parameter(s), how can I modify them before calling?
callback.apply(this);
}
}
How can I modify the argument(s) the user passed in before performing apply or call on the function?
apply takes a second parameter which is a list of arguments to pass to the function. call does the same, except it passes its own argument-list (everything after the first parameter which is used as this).
So, if you know which parameters you expect, you can just add them to the invoking function as the second parameter to apply (or as a list of parameters to call):
this.onSomething = function(arg1, arg2) {
// reverse the first and second arguments
callback.apply(this, [arg2, arg1]);
// equivalent:
callback.call(this, arg2, arg1);
};
If you don't know what kind of arguments to expect, but you still want to do something with them, you can do so with the builtin arguments pseudo-array which holds the arguments given to the current function (even when you don't declare them explicitly).
You can use this to invoke the callback with the same arguments given to the invoking function, or some transformation of them; e.g.:
this.onSomething = function() {
// call callback with the same arguments we got
callback.apply(this, arguments);
// or, make some changes
var newArgs = ["extra argument", arguments[1], arguments[0]];
callback.apply(this, newArgs);
};
Sounds like what you're asking for is fairly simple, see below:
var CustomObject = function() {
this.onSomething = function(callback, param1, param2) {
param1 += 4;
param2 = 'Something about ' + param2 + ' is different...';
callback.apply(this, [param1, param2]);
}
}

Categories