I'm trying to write some js function's validation.
Idea: I have checking function, passing to it checked function object, here make sure that all right (or not). I don't want to pass arguments separately, only function object.
How can i reach this?
For now i forced to be content with two parameters for checking function - func obj and func arguments
That is what i want:
function checkFunction(func) {
console.log(func.name);
console.log(func.arguments);
// validate something
}
function checkedFunction(a, b, c) {
checkFunction(checkedFunction...);
// do something
}
That is what i have now:
function checkFunction(func, args) {
console.log(func.name);
console.log(args);
// validate something
}
function checkedFunction(a, b, c) {
checkFunction(checkedFunction, arguments);
// do something;
}
Related
Here are two callback function:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p)'
}
If I want use callback_a
function test(callback){
if(condition){
callback();
}
}
test(callback_a);
But the function test isn't applicable to callback_b, So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
There are three options:
The easiest way is to use spread operator:
function test(callback, ...callback_args) {
callback(...callback_args);
}
in this case the invocation of test for function callback_b would be like this:
test(callback_b,"b")
The second way is using arguments which are scoped to any function in JavaScript:
function test(callback) {
callback.apply(null, arguments.slice(1));
}
the invocation of test for function callback_b would be the same:
test(callback_b,"b")
Another options is to use partially applied functions. In this case you should define b_callback like this (ES6 syntax):
let callback_b = (p) => () => void{
alert('b says'+ p)'
}
or without ES6:
function callback_b(p) {
return function(){
alert('b says'+ p)'
}
}
and invoke it like this:
test(callback_b("b"))
There is a special object called arguments that gets created when a function is invoked. It's an array-like object that represents the arguments passed in to a function:
It can be used like this:
test();
// no arguments passed, but it still gets created:
// arguments.length = 0
// arguments >> []
test(a);
// ONE argument passed:
// arguments.length = 1
// arguments >> [a]
test(a,b,c,d);
// FOUR arguments passed:
// arguments.length = 4
// arguments >> [a,b,c,d]
Knowing this, one can call a callback with the rest of the arguments passed in from the parent function using apply like this:
function test(callback) {
callback.apply(null, Array.prototype.slice.call(arguments, 1));
}
// arguments passed into test are available in the function scope when
// .slice is used here to only pass the portion of the arguments
// array relevant to the callback (i.e. any arguments minus the
// first argument which is the callback itself.)
//
// N.B. The arguments object isn't an array but an array like object so
// .slice isn't available on it directly, hence .call was used here)
Might be worth reading up on:
The arguments object
Function.prototype.apply, Function.prototype.call and Function.prototype.bind as they are way to bind a context and arguments to a function (i.e. they'll work with the arguments object to call a function where you may not know how many arguments will be passed)
So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
Basically, you don't. The function receiving the callback is in charge of what the callback receives as arguments. When you call Array#forEach, it's Array#forEach that decides what arguments your callback gets. Similarly, String#replace defines what it will call its callback with.
Your job is to say what test will do, what it will call its callback with. Then it's the job of the person using test to write their callback appropriately. For instance: You might document test as calling the callback with no arguments. If the caller wants to use callback_b, then it's up to them to handle the fact that callback_b expects a parameter. There are several ways they can do that:
The could wrap it in another function:
test(function() {
callback_b("appropriate value here");
});
...or use Function#bind
test(callback_b.bind(null, "appropriate value here"));
...but it's their problem, not yours.
Side note: If they pass you callback_b and you call it without any arguments, you won't get an error. JavaScript allows you to call a function with fewer arguments than it expects, or more. How the function handles that is up to the author of the function.
You can pass an anonymous function as the callback that will itself return your desired callback function with parameters.
test(function() { return callback_b(' how are you'); });
see this working snippet that will first use callback_a, then callback_b (with parameter) as the callback:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p);
}
function test(callback){
if(true){
callback();
}
}
test(callback_a);
test(function() { return callback_b(' how are you'); });
You can pass the parameter while calling the callback
function test(callback){
if(condition){
callback();
}
else if(other condition){
callback("b");
}
}
test(callback_b);
You can write your callback function like
function callback_a_b(){
if(arguments.length){
var arg = [].slice.call(arguments);
alert('b says'+ arg[0])
}
else{
alert('a');
}
}
You can pass array of parameters as second param of test function or in ES6 use spread operator read more here
function test(callback, params){
if(condition){
if (params === undefined){
callback();
} else {
callback.apply(null, params); //params must be array
//ES6: callback(...params);
}
}
}
test(callback_a);
test(callback_b, [" whatever"]);
I've just checked in my browser (ffox 51.0.1) that the following works:
function test(callback,other_args){if(condition){callback(other_args);}}
results:
condition=true
test(callback_a)
=> shows the alert with 'a'
condition=false
test(callback_a)
=> doesn't show anything
condition=true
test(callback_b,"pepe")
=> shows the alert with 'b sayspepe'
condition=false
test(callback_b,"pepe")
=> doesn't show anything
In following example is a typical function which accepts three parameters:
function cook(a, b, c) {
// cooking stuff..
return[results];
};
Or as a property function, like this:
var myApp = {
handler: function(a, b, c) {
// actions
}
};
My question is how to call this function properly, if we want to pass
only two parameters, or even only one:
Like this:
cook(param1, param2); - myApp.handler(param1, param2);
Or we have to pass always the same number of parameters the function
accepts, regardless if they have data or not, like this:
cook(param1, param2, ""); - myApp.handler(param1, param2, "");
Also, what is the proper way if we want to pass the first and the third
parameters? Or only the second or the third parameter. I can't think
something other than this:
cook(param1, "", param3); - myApp.handler(param1, "", param3);
cook("", param2, "");
cook("", "", param3);
Is this correct and the only way to do it?
the best way as you describe your function would be using an object as a parameter
// function definition
function cook(object) {
//do stuff
return object.a + " " + object.b + " " + object.c;
}
parameters1 = {a:"paramA", b:"paramB",c:"paramC"}
cook(parameters1) // returns paramA paramB paramC
if you test those parameters in the function to handle cases where a, b or c might be null or undefined, you can then do
cook({a:"paramAonly"})
or
cook({a:"paramA", b:"paramB"})
"My question is how to call this function properly, if we want to pass
only two parameters, or even only one"
Ok. This question has multiple ways to interpret what you are asking, so I will give you one of those ways.
If you want to have a function that takes a varying number of parameters then you should use es6 spread operator like this
function (...args) { // the ... is the spread operator
return args
}
Now if you want to return always the first item in the parameters you do this:
function (...args) {
return args[0]
}
Lastly if you want to do something that is more flexible then you can offer this method:
function (... args) {
if(args.length === 3) {
return // you get the point?
}
}
Does this answer your question?
Do we have to pass always the same number of parameters the function ?
No, you don't have to pass all the parameters to a function, the arguments you don't set will be undefined
function cook(a, b, c) {
console.log(a, b, c)
};
cook('foo') // will console log 'foo undefined undefined'
What is the proper way if we want to pass the first and the third
parameters?
You were right on this one, although you would generally give undefined or null value as a parameter here rather than an empty string. If you want to ignore param2 but give param3 for example
cook(param1, null, param3)
Then you could test if all the parameters are properly set or not in your function with a more standard null or undefined value
For example if I have a function defined like this:
function set_name(name) {
return name;
}
But I call it this way (for personal reasons this way):
set_name.apply(this, ['Duvdevan', 'Omerowitz']);
What would be the best practice to check and prevent set_name function's further execution if it had accepted more than 1 parameter / argument?
Also, I have to note that I'm trying to figure out a way to check this before I call .apply function:
function set_name(name) {
// if(arguments.length > 1) console.log('This is not what I need though.');
return name;
}
var args = ['Duvdevan', 'Omerowitz'];
// Now, at this point I'd like to check if the set_name function can accept two arguments.
// If not, I'd console.log or return new Error here.
set_name.apply(this, args);
Is this doable?
You can get the number of arguments expected by the function via Function.length:
function set_name(name) {
return name;
}
var args = ['Duvdevan', 'Omerowitz'];
console.log(set_name.length); // 1
if (args.length > set_name.length) {
throw new Error('Too many values'); // throws
}
set_name.apply(this, args); // not executed
I have a lot of my code inside an object literal and there are a couple functions where I want to be able to pass the functions arguments for the parameters but I can't figure out how to do that.
Here is an example of my object..
var test = {
button: $('.button'),
init: function() {
test.button.on('click', this.doSomething);
},
doSomething: function(event, param1, param2) {
console.log(param1);
console.log(param2);
}
};
So when the button is clicked and it calls the function doSomething I want to pass in arguments for param1 and param2.
Something similar to this maybe, but this does not work.
test.button.on('click', this.doSomething('click', 'arg1', 'arg2'));
Any ideas, or am I going about this the wrong way?
The jQuery.proxy() function seems to be exactly what you need. Have a good read at the docs and see if they make sense to you. For your specific example,
var test = {
button: $('.button'),
init: function() {
test.button.on('click', $.proxy(this.doSomething, null, 'arg1', 'arg2');
},
doSomething: function(param1, param2, event) {
console.log(param1);
console.log(param2);
}
};
In this example, the parameters to $.proxy are:
this.doSomething - The the function to call
null - The context in which the function will be called. By supplying null, we are saying to use its 'normal' context.
arg1 - The value of param1 formal parameter of the called function
arg2 - The value of param2 formal parameter of the called function
Since the click callback supplied the final parameter (event), that is already provided and doesn't need to be additionally or explicitly declared. The jQuery.proxy() when passed additional parameters passes those at the front of the formal parameter list and any remaining parameters implicitly supplied are passed at the end. So if we a function that looks like:
var f = function(a, b, c) {
console.log(a, b, c);
};
and invoke it through a proxy:
var p = $.proxy(f, null, 2, 3);
p(1);
The value of a, b and c that are logged will be 2,3,1.
This question is also extremely close to this one.
How can I pass arguments to event handlers in jQuery?
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]);
}
}