Passing variable number of arguments from one function to another [duplicate] - javascript

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Is it possible to send a variable number of arguments to a JavaScript function?
I can use arguments to get a variable number of arguments within a function, but how can I pass them to another function without knowing its prototype?
function show(foo, bar) { window.alert(foo+' '+bar); }
function run(f) { f(arguments); } // not correct, what to do?
run(show, 'foo', 'bar');
Note: I cannot guarantee the number of arguments needed for the function f that is passed to run. Meaning, even though the example shown has 2 arguments, it could be 0-infinite, so the following isn't appropriate:
function run(f) { f(arguments[1], arguments[2]); }

The main way to pass a programmatically generated set of arguments to a function is by using the function's 'apply' method.
function show(foo, bar) {
window.alert(foo+' '+bar);
}
function run(f) {
// use splice to get all the arguments after 'f'
var args = Array.prototype.splice.call(arguments, 1);
f.apply(null, args);
}
run(show, 'foo', 'bar');

You can in fact do this with apply, if I understand your question correctly:
function show(foo, bar) { window.alert(foo+' '+bar); }
function run(f, args) { f.apply(null,args); }
run(show, ['foo', 'bar']);

you need to use the apply function.. here is how u do it:
function variableFunction1()
{
alert("variableFunction1 arguments length: " + arguments.length);
// calls second varargs function keeping current 'this'.
variableFunction2.apply(this, arguments);
}
function variableFunction2()
{
alert("variableFunction2 arguments length: " + arguments.length);
}
variableFunction1('a','b','c');
Demo

In your example to pass variable arguments to show this works
function show(foo, bar) { window.alert(foo+' '+bar); }
function run(f) { f.apply(null, Array().slice.call(arguments, 1)); }
run(show, 'foo', 'bar');

Related

Send function parameters as arguments to another function js [duplicate]

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

Assignment of this keyword

I understand that when invoking a method of an object, the this value in that method is assigned to the object itself.
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
dog.growOneYear();
dog.age; // 6
That makes sense to me. However, when I try it a different way, it doesn't work:
function invokeTwice(cb) {
cb();
cb();
}
invokeTwice(dog.growOneYear);
dog.age;
// still 6
Why didn't this work?
On the second example there is no object used to invoke growOneYear().
You copy the method growOneYear into a new variable (cb). This looses the link between dog and growOneYear. You then call cb() as a regular function, without using an object to serve as this inside the method.
You said in the question:
... when invoking a method of an object, the this value in that method is assigned to the object itself.
The statement above is correct and it explains why it doesn't work when you invoke growOneYear as cb(): it is "a method of an object" only when it is invoked as dog.growOneYear(). If you copy it into cb and call it as cb() it is just a regular function; no object is used to call it any more.
The situation you describe in the questoin is even listed in the documentation of Function.prototype.bind():
A common mistake for new JavaScript programmers is to extract a method from an object, then to later call that function and expect it to use the original object as its this (e.g. by using that method in callback-based code). Without special care, however, the original object is usually lost.
Function.prototype.bind() is the solution to your problem:
const dog = {
age: 5,
growOneYear: function () {
this.age += 1;
}
};
dog.growOneYear();
console.log(dog.age); // 6
function invokeTwice(cb) {
cb();
cb();
}
// Bind the method to the desired object
// Inside `invokeTwice()`, `cb` is the function `dog.growOneYear` with
// `this` pointing to `dog` ------+
// v
invokeTwice(dog.growOneYear.bind(dog));
console.log(dog.age); // 8
In the second case,your function will be passed the window object as the this parameter since you are not invoking the cb function passed with any specified object.
In case you want to invoke with your dog object use apply like this.
enter code here
const dog = {
age: 5,
growOneYear: function () {
//console.log(this)
this.age += 1;
}
};
dog.growOneYear();
dog.age;
function invokeTwice(cb) {
cb.apply(dog);
cb.apply(dog);
}
invokeTwice(dog.growOneYear);
console.log(dog.age);

Calling functions with parameters in javascript

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

How can I get the arguments for function that I pass as a parameter to another function?

I'm trying to access arguments from the function that is being passed as a parameter to make sure to pass those through, however I'm getting the error:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
How can I get the arguments for func so that I can pass the message through when calling it?
UPDATE: maybe a better question is why can't I log func.arguments?
function a(func){
console.log(func.arguments)
return function(){
func.apply(null, func.arguments);
}
}
function log(message){
console.log(message)
}
a(log.bind('hello'))()
It can be done but its not pretty. you have to return an object with the parameters you want from the function you are passing
function a(func){
console.log(func.arguments)
//you dont actually need this since you have already called the function before passing
//return function(){
// func.apply(null, func.arguments);
//}
}
function log(message){
console.log(message);
return {
arguments : arguments //store the arguments and return them
};
}
//you not actually passing the function here (the way you have it written)
//you are calling it and returning its value
//we return { arguments: arguments } so the "a" function can retrieve its properties.
a(log("hello"));
I guess what you're trying to do is to intercept a function with a "logging" function.
If so, there is a different approach, using ES6 rest params --
let withLog = func => (...args) => {
console.log(...args);
return func(...args);
};
let add = (x, y) => x + y;
withLog(add)(2, 3); // outputs '2 3', returns 5

Javascript - add parameters to a function passed as a parameter

Here is the behaviour I'm looking for:
function one(func){
func(5);
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(two(3)) //prints 3, 5
Can this behaviour or something similar be accomplished in javascript?
You can always use the bind() function to pass some arguments to your function. It'll create a new function with the first argument - arg1 - equal to the value of 3 in this example:
function one(func){
func(5);
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(two.bind(null, 3))
You can read more about the bind() function here: MDN - Bind
Some workaround is possible
function one() {
var args = Array.prototype.slice.call(arguments);
var func = args[0];
args.splice(0, 1);
args.push(5);
func.apply(this, args);
}
function two(arg1, arg2) {
console.log(arg1);
console.log(arg2);
}
one(two, 3)
There's a problem with your syntax: function one is expecting its single argument to be a function. Then, below, when you invoke it, you are not passing the function two, but whatever two returns when it's passed a single argument, probably undefined. I don't know what specifically you're trying to accomplish but I'd recommend a little research into closures.
function one(arg){
two(arg, 5); // func here is two so it requires two params...
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(3)// one expect function so can't execute function here!
as soon as one expects function as argument two(3) should return function.
this condition is required
so in order to achieve it your two function should be
function two(arg1){
console.log(arg1);
return function(arg2) {
console.log(arg2);
};
}
so two(3) function call gets passed as argument to one
so before assigning value to variable engine executes it. And execution of two(3) call logs 3 to console and returns function
function(arg2) {
console.log(arg2);
};
and then engine assigns executed value(returned function) to func variable.
so func parameter of one function now looks like
func = function(arg2) {
console.log(arg2);
};
one calls func with 5 passed in as argument.
so 5 gets logged to console.
Basically you can't specify the parameter in the function or it'll run. You need to specify the function aka one(two), but that obviously wouldn't work.
However if you dynamically create a function you should be able to accomplish the task like so :
function one(func){
func(5);
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(function(val) { two(3, val) }) //prints 3, 5
well somehow this things work for me
function one(func){
console.log("one is running");
func;
}
function two(args1, args2){
console.log("two is running");
console.log("args1 -> " + args1);
console.log("args2 -> " + args2);
}
//to call it
args1 = 6;
args2 = 12
one(two(args1,args2));

Categories