How to call a function by an array of argument values? [duplicate] - javascript

I've tried the following with no success:
function a(args){
b(arguments);
}
function b(args){
// arguments are lost?
}
a(1,2,3);
In function a, I can use the arguments keyword to access an array of arguments, in function b these are lost. Is there a way of passing arguments to another javascript function like I try to do?

Use .apply() to have the same access to arguments in function b, like this:
function a(){
b.apply(null, arguments);
}
function b(){
console.log(arguments); //arguments[0] = 1, etc
}
a(1,2,3);
You can test it out here.

Spread operator
The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) are expected.
ECMAScript ES6 added a new operator that lets you do this in a more practical way: ...Spread Operator.
Example without using the apply method:
function a(...args){
b(...args);
b(6, ...args, 8) // You can even add more elements
}
function b(){
console.log(arguments)
}
a(1, 2, 3)
Note This snippet returns a syntax error if your browser still uses ES5.
Editor's note: Since the snippet uses console.log(), you must open your browser's JS console to see the result - there will be no in-page result.
It will display this result:
In short, the spread operator can be used for different purposes if you're using arrays, so it can also be used for function arguments, you can see a similar example explained in the official docs: Rest parameters

The explanation that none of the other answers supplies is that the original arguments are still available, but not in the original position in the arguments object.
The arguments object contains one element for each actual parameter provided to the function. When you call a you supply three arguments: the numbers 1, 2, and, 3. So, arguments contains [1, 2, 3].
function a(args){
console.log(arguments) // [1, 2, 3]
b(arguments);
}
When you call b, however, you pass exactly one argument: a's arguments object. So arguments contains [[1, 2, 3]] (i.e. one element, which is a's arguments object, which has properties containing the original arguments to a).
function b(args){
// arguments are lost?
console.log(arguments) // [[1, 2, 3]]
}
a(1,2,3);
As #Nick demonstrated, you can use apply to provide a set arguments object in the call.
The following achieves the same result:
function a(args){
b(arguments[0], arguments[1], arguments[2]); // three arguments
}
But apply is the correct solution in the general case.

If you want to only pass certain arguments, you can do so like this:
Foo.bar(TheClass, 'theMethod', 'arg1', 'arg2')
Foo.js
bar (obj, method, ...args) {
obj[method](...args)
}
obj and method are used by the bar() method, while the rest of args are passed to the actual call.

This one works like a charm.
function a(){
b(...arguments);
}
function b(){
for(var i=0;i<arguments.length;i++){
//you can use arguments[i] here.
}
}
a(1,2,3);

Related

what is this function for? [duplicate]

This snippet is cut from Secrets of the JavaScript Ninja.
function log() {
try {
console.log.apply( console, arguments );
} catch(e) {
try {
opera.postError.apply( opera, arguments );
} catch(e){
alert( Array.prototype.join.call( arguments, " " ) );
}
}
}
Why should I use apply and what's the difference between console.log.apply(console, arguments) and console.log(arguments)?
In this case, the log function may accept any number of arguments.
Using .apply(), it doesn't matter how many arguments are passed. You can give the set to console.log(), and they will arrive as individual arguments.
So if you do:
console.log(arguments)
...you're actually giving console.log a single Arguments object.
But when you do:
console.log.apply( console, arguments );
...it's as though you passed them separately.
Other useful examples of using .apply() like this can be demonstrated in other methods that can accept a variable number of arguments. One such example is Math.max().
A typical call goes like this:
var max = Math.max( 12,45,78 ); // returns 78
...where it returns the largest number.
What if you actually have an Array of values from which you need the largest? You can use .apply() to pass the collection. Math.max will think they were sent as separate arguments instead of an Array.
var max = Math.max.apply( null, [12,45,92,78,4] ); // returns 92
As you can see, we don't need to know in advance how many arguments will be passed. The Array could have 5 or 50 items. It'll work either way.
If you have
function log() {
console.log.apply(console, arguments);
}
and call it like log('foo'); then that translates to console.log.apply(console, ['foo']); which is equivalent to console.log('foo'); which is what you want.
If you defined it like
function log() {
console.log(arguments);
}
instead then log('foo'); would be equivalent to log(['foo']); which is not what you want.
The apply function changes the value of this in the callee as well as letting you pass an array for the arguments.
For example, if you want to pass an array as arguments to a function:
function foo(value1, value2, value3) {
alert("Value 1 is "+value1+".");
alert("Value 2 is "+value2+".");
alert("Value 3 is "+value3+".");
}
var anArray=[1, 2, 3];
foo(anArray); // This will not work. value1 will be anArray, and value 2 and 3 will be undefined.
foo.apply(this, anArray); // This works, as anArray will be the arguments to foo.
Or, another use: changing this:
function Foo() {
this.name="world";
this.sayHello=function() {
alert("Hello, "+this.name);
};
}
var foo=new Foo();
foo.sayHello(); // This works, as this will be foo in foo's sayHello.
var sayHello=foo.sayHello;
sayHello(); // This does not work, as this will not be foo.
sayHello.apply(foo, []); // This will work, as this will be foo.
Let's discuss some background, why apply exist specially when we have call method with similar syntax.
First we need to understand some topics:
Variadic function:
In computer programming it is a function which can accept any number
of arguments.
Data structure is JavaScript:
In javascript if we are dealing with data than most commonly used data structures is array, and in most cases we get data in the form of array.
Now if we are executing any variadic function in javascript than our call will look like this -
average is a variadic function, and its call will look like,
average(1,2,3);
average(1);
average(3,5,6,7,8);
and if are getting data in array format(in most of the cases we will get data in array format for Variadic functions), than we need to call our function like -
average(array[0],array[1],array[2],array[3],.... and so on)
What if we got an array with length 100 items, do we write it like this ?
No, We have apply method, which was designed just for this purpose.
average.apply(null,array);
console.log(arguments) would send a single argument to console.log, the array of arguments passed to your log method. console.log.apply(console, arguments) sends the possibly multiple arguments as multiple arguments instead of a single array.

Want to understand code flow of console.log.call.call.call.call.call.apply(a => a, [1, 2]);

I know how call and apply methods works, but coming to this question, it's some what tricky for me to understand the flow. Can someone help me to understand this.
console.log.call.call.call.call.call.apply(a => a, [1, 2]);
First off, note that in JavaScript, functions are objects, and can have their own properties (which you can access with the . notation just like any other object). Of the properties that exist on all functions are .apply and .call, which are themselves functions.
Second, both .call and .apply provide a mechanism to invoke a function with a specified this value. To understand that, this normally refers to whatever is on the left-hand side of the . when you call a function as a method of an object, e.g. when you call foo.bar(1, 2, 3), within the context of bar, this will refer to foo. So it's also possible to use bar.call(foo, 1, 2, 3) or bar.apply(foo, [1, 2, 3]) to achieve a similar effect; in both cases the first argument becomes this.
So basically, the console.log and all the .call's except the last one don't actually matter. The initial part of the code is just trying to apply .call on some function, and could just as easily be replaced with Function.prototype.call.apply
Skipping ahead a bit, a => a is an arrow function, short-hand for function(a) { return a; }. It creates an anonymous function that accepts one argument and returns that same argument as result. Note that this function doesn't actually refer to this so all the previous calls to .call and .apply are pretty much irrelevant.
Last [1, 2] is just an array literal containing two items, 1 and 2 which be unrolled as arguments by .apply.
So breaking it down:
console.log.call.call.call.call.call.apply(a => a, [1, 2]);
Can be reduced to:
Function.prototype.call.apply(a => a, [1, 2]);
Can be reduced to:
var fn = a => a;
fn.call(1, 2);
.call will use its first argument as the this value of the function and pass subsequent values as parameters, and since fn doesn't use this, we can further reduce it to:
var fn = a => a;
fn(2);
Since fn is just a simple identity function, the result is just:
2;
It doesn't do anything, apply will return 1 and 2 as separate values which call won't do anything with since it's not a function. Even if you were to do console.log.call.call(a => a, [1,2]) you wouldn't get anything since the first call returns just the array [1,2] which isn't a function the second call can do anything with...
console.log.call.call./*and so on*/call./*returns nothing eval({1, 2}) =
undefined*/call./*returns {1, 2} up the chain*/apply(a => a, [1,2]);
//You can try this which also won't return anything:
console.log.call([1,2]);
Although oddly enough I was expecting this sort of code to throw an error, which it didn't, it just didn't log or throw an error. So it really doesn't return anything...

How is this function able to return a single object?

function list() {
return Array.prototype.slice.call(arguments, 0); //Here is my problem
}
var list1 = list(1, 2, 3); // [1, 2, 3]
I found this example of the use of the slice function online and would like some help understanding how the call() function is able to access each item which is passed to list through the use of the "arguments" property.
I don't understand how passing the single "arguments" property as the function context to slice() works. Am I missing some recursion going on in there somewhere?
The function turns the provided arguments object into an array.
When a function's call method is called, then this within the function is set to the supplied object. Another way to call it is:
[].slice.call(arguments);
so inside the slice method, this isn't the empyt array at the start of the expression, but the arguments object.
This isn't a good idea if the object passed is not a native object (e.g. a DOM object like a NodeList or similar) as it will fail on some browsers in use. Also, this isn't "context", it's simply one parameter of a function's execution context.

Why should use "apply"?

This snippet is cut from Secrets of the JavaScript Ninja.
function log() {
try {
console.log.apply( console, arguments );
} catch(e) {
try {
opera.postError.apply( opera, arguments );
} catch(e){
alert( Array.prototype.join.call( arguments, " " ) );
}
}
}
Why should I use apply and what's the difference between console.log.apply(console, arguments) and console.log(arguments)?
In this case, the log function may accept any number of arguments.
Using .apply(), it doesn't matter how many arguments are passed. You can give the set to console.log(), and they will arrive as individual arguments.
So if you do:
console.log(arguments)
...you're actually giving console.log a single Arguments object.
But when you do:
console.log.apply( console, arguments );
...it's as though you passed them separately.
Other useful examples of using .apply() like this can be demonstrated in other methods that can accept a variable number of arguments. One such example is Math.max().
A typical call goes like this:
var max = Math.max( 12,45,78 ); // returns 78
...where it returns the largest number.
What if you actually have an Array of values from which you need the largest? You can use .apply() to pass the collection. Math.max will think they were sent as separate arguments instead of an Array.
var max = Math.max.apply( null, [12,45,92,78,4] ); // returns 92
As you can see, we don't need to know in advance how many arguments will be passed. The Array could have 5 or 50 items. It'll work either way.
If you have
function log() {
console.log.apply(console, arguments);
}
and call it like log('foo'); then that translates to console.log.apply(console, ['foo']); which is equivalent to console.log('foo'); which is what you want.
If you defined it like
function log() {
console.log(arguments);
}
instead then log('foo'); would be equivalent to log(['foo']); which is not what you want.
The apply function changes the value of this in the callee as well as letting you pass an array for the arguments.
For example, if you want to pass an array as arguments to a function:
function foo(value1, value2, value3) {
alert("Value 1 is "+value1+".");
alert("Value 2 is "+value2+".");
alert("Value 3 is "+value3+".");
}
var anArray=[1, 2, 3];
foo(anArray); // This will not work. value1 will be anArray, and value 2 and 3 will be undefined.
foo.apply(this, anArray); // This works, as anArray will be the arguments to foo.
Or, another use: changing this:
function Foo() {
this.name="world";
this.sayHello=function() {
alert("Hello, "+this.name);
};
}
var foo=new Foo();
foo.sayHello(); // This works, as this will be foo in foo's sayHello.
var sayHello=foo.sayHello;
sayHello(); // This does not work, as this will not be foo.
sayHello.apply(foo, []); // This will work, as this will be foo.
Let's discuss some background, why apply exist specially when we have call method with similar syntax.
First we need to understand some topics:
Variadic function:
In computer programming it is a function which can accept any number
of arguments.
Data structure is JavaScript:
In javascript if we are dealing with data than most commonly used data structures is array, and in most cases we get data in the form of array.
Now if we are executing any variadic function in javascript than our call will look like this -
average is a variadic function, and its call will look like,
average(1,2,3);
average(1);
average(3,5,6,7,8);
and if are getting data in array format(in most of the cases we will get data in array format for Variadic functions), than we need to call our function like -
average(array[0],array[1],array[2],array[3],.... and so on)
What if we got an array with length 100 items, do we write it like this ?
No, We have apply method, which was designed just for this purpose.
average.apply(null,array);
console.log(arguments) would send a single argument to console.log, the array of arguments passed to your log method. console.log.apply(console, arguments) sends the possibly multiple arguments as multiple arguments instead of a single array.

JavaScript variable number of arguments to function

Is there a way to allow "unlimited" vars for a function in JavaScript?
Example:
load(var1, var2, var3, var4, var5, etc...)
load(var1)
Sure, just use the arguments object.
function foo() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
In (most) recent browsers, you can accept variable number of arguments with this syntax:
function my_log(...args) {
// args is an Array
console.log(args);
// You can pass this array as parameters to another function
console.log(...args);
}
Here's a small example:
function foo(x, ...args) {
console.log(x, args, ...args, arguments);
}
foo('a', 'b', 'c', z='d')
=>
a
Array(3) [ "b", "c", "d" ]
b c d
Arguments
​ 0: "a"
​1: "b"
​2: "c"
​3: "d"
​length: 4
Documentation and more examples here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters
Another option is to pass in your arguments in a context object.
function load(context)
{
// do whatever with context.name, context.address, etc
}
and use it like this
load({name:'Ken',address:'secret',unused:true})
This has the advantage that you can add as many named arguments as you want, and the function can use them (or not) as it sees fit.
I agree with Ken's answer as being the most dynamic and I like to take it a step further. If it's a function that you call multiple times with different arguments - I use Ken's design but then add default values:
function load(context) {
var defaults = {
parameter1: defaultValue1,
parameter2: defaultValue2,
...
};
var context = extend(defaults, context);
// do stuff
}
This way, if you have many parameters but don't necessarily need to set them with each call to the function, you can simply specify the non-defaults. For the extend method, you can use jQuery's extend method ($.extend()), craft your own or use the following:
function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
}
This will merge the context object with the defaults and fill in any undefined values in your object with the defaults.
It is preferable to use rest parameter syntax as Ramast pointed out.
function (a, b, ...args) {}
I just want to add some nice property of the ...args argument
It is an array, and not an object like arguments. This allows you to apply functions like map or sort directly.
It does not include all parameters but only the one passed from it on. E.g. function (a, b, ...args) in this case args contains
argument 3 to arguments.length
Yes, just like this :
function load()
{
var var0 = arguments[0];
var var1 = arguments[1];
}
load(1,2);
As mentioned already, you can use the arguments object to retrieve a variable number of function parameters.
If you want to call another function with the same arguments, use apply. You can even add or remove arguments by converting arguments to an array. For example, this function inserts some text before logging to console:
log() {
let args = Array.prototype.slice.call(arguments);
args = ['MyObjectName', this.id_].concat(args);
console.log.apply(console, args);
}
Although I generally agree that the named arguments approach is useful and flexible (unless you care about the order, in which case arguments is easiest), I do have concerns about the cost of the mbeasley approach (using defaults and extends). This is an extreme amount of cost to take for pulling default values. First, the defaults are defined inside the function, so they are repopulated on every call. Second, you can easily read out the named values and set the defaults at the same time using ||. There is no need to create and merge yet another new object to get this information.
function load(context) {
var parameter1 = context.parameter1 || defaultValue1,
parameter2 = context.parameter2 || defaultValue2;
// do stuff
}
This is roughly the same amount of code (maybe slightly more), but should be a fraction of the runtime cost.
While #roufamatic did show use of the arguments keyword and #Ken showed a great example of an object for usage I feel neither truly addressed what is going on in this instance and may confuse future readers or instill a bad practice as not explicitly stating a function/method is intended to take a variable amount of arguments/parameters.
function varyArg () {
return arguments[0] + arguments[1];
}
When another developer is looking through your code is it very easy to assume this function does not take parameters. Especially if that developer is not privy to the arguments keyword. Because of this it is a good idea to follow a style guideline and be consistent. I will be using Google's for all examples.
Let's explicitly state the same function has variable parameters:
function varyArg (var_args) {
return arguments[0] + arguments[1];
}
Object parameter VS var_args
There may be times when an object is needed as it is the only approved and considered best practice method of an data map. Associative arrays are frowned upon and discouraged.
SIDENOTE: The arguments keyword actually returns back an object using numbers as the key. The prototypal inheritance is also the object family. See end of answer for proper array usage in JS
In this case we can explicitly state this also. Note: this naming convention is not provided by Google but is an example of explicit declaration of a param's type. This is important if you are looking to create a more strict typed pattern in your code.
function varyArg (args_obj) {
return args_obj.name+" "+args_obj.weight;
}
varyArg({name: "Brian", weight: 150});
Which one to choose?
This depends on your function's and program's needs. If for instance you are simply looking to return a value base on an iterative process across all arguments passed then most certainly stick with the arguments keyword. If you need definition to your arguments and mapping of the data then the object method is the way to go. Let's look at two examples and then we're done!
Arguments usage
function sumOfAll (var_args) {
return arguments.reduce(function(a, b) {
return a + b;
}, 0);
}
sumOfAll(1,2,3); // returns 6
Object usage
function myObjArgs(args_obj) {
// MAKE SURE ARGUMENT IS AN OBJECT OR ELSE RETURN
if (typeof args_obj !== "object") {
return "Arguments passed must be in object form!";
}
return "Hello "+args_obj.name+" I see you're "+args_obj.age+" years old.";
}
myObjArgs({name: "Brian", age: 31}); // returns 'Hello Brian I see you're 31 years old
Accessing an array instead of an object ("...args" The rest parameter)
As mentioned up top of the answer the arguments keyword actually returns an object. Because of this any method you want to use for an array will have to be called. An example of this:
Array.prototype.map.call(arguments, function (val, idx, arr) {});
To avoid this use the rest parameter:
function varyArgArr (...var_args) {
return var_args.sort();
}
varyArgArr(5,1,3); // returns 1, 3, 5
Use the arguments object when inside the function to have access to all arguments passed in.
Be aware that passing an Object with named properties as Ken suggested adds the cost of allocating and releasing the temporary object to every call. Passing normal arguments by value or reference will generally be the most efficient. For many applications though the performance is not critical but for some it can be.
Use array and then you can use how many parameters you need. For example, calculate the average of the number elements of an array:
function fncAverage(sample) {
var lenghtSample = sample.length;
var elementsSum = 0;
for (var i = 0; i < lenghtSample; i++) {
elementsSum = Number(elementsSum) + Number(sample[i]);
}
average = elementsSum / lenghtSample
return (average);
}
console.log(fncAverage([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); // results 5.5
let mySample = [10, 20, 30, 40];
console.log(fncAverage(mySample)); // results 25
//try your own arrays of numbers

Categories

Resources