This question already has answers here:
Passing arguments forward to another javascript function
(5 answers)
Closed 7 years ago.
I have this call:
function del(Model, modelName, model_id, req, res, next, cb) {
if(req.query.optimisticDelete){
optimisticDelete(arguments);
}
else{
pessimisticDelete(arguments);
}
}
the problem of course, is that the arguments aren't passed correctly to the optimisticDelete and pessimisticDelete functions.
in an ideal JS world, this might work, but I can easily see why it doesn't.
But it doesn't take away from that fact that I just didn't want to type all the arguments out for each call, and in fact I wanted to omit the arguments in the del function signature also, so this would be the most ideal situation, although I need a reference to the req object, which I am now missing:
function del() {
if(req.query.optimisticDelete){
optimisticDelete(arguments);
}
else{
pessimisticDelete(arguments);
}
}
but of course, when arguments is passed, it does not seem to magically separate into separate arguments.
And also, this doesn't work either:
function del(Model, modelName, model_id, req, res, next, cb) {
if(req.query.optimisticDelete){
optimisticDelete(Array.prototype.slice.call(arguments));
}
else{
pessimisticDelete(Array.prototype.slice.call(arguments));
}
}
if you understand what I am trying to do, please let me know if it's possible and how,
You're calling optimisticDelete with a single argument, which is an arguments special object that contains all the arguments to the original call. If you want to spread them out, you need to use apply:
optimisticDelete.apply(null, arguments);
It's not necessary to convert arguments to an array first. MDN says:
You can also use arguments for the argsArray parameter. arguments is a local variable of a function. It can be used for all unspecified arguments of the called object. Thus, you do not have to know the arguments of the called object when you use the apply method. You can use arguments to pass all the arguments to the called object.
There are two problems. First, the arguments object is weird, but it's not that weird. Nothing magically turns into a parameter list across a simple function call. There is, however, Function.prototype.apply, which is ultimately what you want.
First however you'll want to turn arguments into a plain array:
var plainArgs = Array.prototype.slice.call(arguments);
Or, if you care about runtime efficiency:
var plainArgs = [];
for (var i = 0; i < arguments.length; ++i)
plainArgs[i] = arguments[i];
(Note — this step may not be necessary, but passing the arguments object out of a function tends to make optimizers throw up their little hands and give up on your functions.)
(Another note — totally ignore this stuff about passing arguments to .apply() being bad. I'm wrong.)
With that out of the way, you can use .apply():
optimisticDelete.apply(null, plainArgs);
The .apply() function expects its second argument to be an array. That array becomes the individual arguments to the function. The first argument to .apply() is the value you'd like this to take on in the function call; since you were calling optimisticDelete() "naked" in the original code, I passed null, but you can pass whatever you want.
Related
I’d like to know both for regular all-in-the-family JS developer-defined functions, as well as predefined DOM methods: what happens if I try to call IE’s attachEvent with the signature of the WHATWG’s addEventListener? For instance:
elem.attachEvent('onbillgates\'mom', function(e){ this.mount(); }, false);
Specifically, note the third argument false. Will that trip anything up, even though the attachEvent method’s signature only calls for two arguments?
What about this example?
function foo(FirstOf2, SecondOf2) {
console.log(FirstOf2 + SecondOf2);
}
foo(1, 2, true);
JavaScript doesn't have the concept of a fixed parameter list. For your own functions you can always specify as many parameters as you want and pass in as many as you want which ever type you want.
For built-in functions, which correlate to native code, it depends.
You asked on what it depends:
Let's look at the ECMA-262
Section 15 about built-in (not to confuse with host) functions in general
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given fewer arguments than the function is specified to require, the function or constructor shall behave exactly as if it had been given sufficient additional arguments, each such argument being the undefined value.
Alright. If I pass in less arguments than needed, it depends on the spec of the function itself (scroll down section 15 to find the spec for each built-in function).
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given more arguments than the function is specified to allow, the extra arguments are evaluated by the call and then ignored by the function. However, an implementation may define implementation specific behaviour relating to such arguments as long as the behaviour is not the throwing of a TypeError exception that is predicated simply on the presence of an extra argument.
Passing in too many arguments should never raise a TypeError. But still it may raise other errors. Again, it depends on the function you talk about.
You were talking explicitly about the DOM and not about built-in functions. To be honest I can't find the corresponding parts of the spec. The ECMA spec is so much easier to read then the w3 website.
Won't hurt. You can even call a function with less parameters than it takes, as long as the function code is ok with a few undefined values.
I came across this important, however old, question; and I hope it'll be beneficial for future generations to share my experiments with it:
One can use the arguments object in order to access a function's arguments, regardless of the amount of arguments in the function's signature.
It's worth mentioning that this doesn't apply to arrow functions:
function singleArg(x) {
console.log(arguments);
}
singleArg(1, 2); // Called with 2 arguments
singleArg(); // Called with 0 arguments
// Results in an error, as 'arguments' isn't defined for arrow functions
((arg) => console.log(arguments))(1);
It's stated in the documentation that arguments isn't exactly an Array:
“Array-like” means that arguments has a length property and properties indexed from zero, but it doesn't have Array's built-in methods like forEach() and map().
Hence the following code results in an error:
(function singleArg(x) {
console.log(arguments); // This line works
arguments.forEach(x => console.log(x)); // This causes an error
})(1, 2);
Upon calling a function with less arguments than in its signature, they're assigned undefined:
(function twoArgs(a, b) {
console.log(`a: ${a}\nb: ${b}`);
})(1);
Try taking a look at this post and perhaps this one.
From MDN:
The arguments object is a local variable available within all
functions; arguments as a property of Function can no longer be used.
You can refer to a function's arguments within the function by using
the arguments object. This object contains an entry for each argument
passed to the function, the first entry's index starting at 0.
You can use the arguments object if you call a function with more
arguments than it is formally declared to accept. This technique is
useful for functions that can be passed a variable number of
arguments.
function myConcat(separator) {
var result = "";
// iterate through non-separator arguments
for (var i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
I don't think it will mess anything up unless you are explicitly dealing with the implicit arguments array. Why are you doing this though?
I am having a hard time understanding the arguments object. In this code, what function would arguments be looking at?
getAjax('get_info', 'java_array', realVarName, cld.listArray, 0, '',
'no_restrict', function() {
show_list(arguments[0], sld);
if (typeof(postFunc) == "function") {
postFunc();
}
});
'arguments' is an inherit variable with any function. It contains all the parameters passed to a function. For instance, a function definition may not list any parameters, however an invocation could include 'n' parameters on it. The function could then still access all of them through the arguments array.
The arguments object is an Array-like object corresponding to the arguments passed to a function.
In your code the arguments[0] will primarily be undefined
First off, arguments is an object that represents the set of arguments passed to the current function. It exists automatically inside of every function. You can read more about it here on MDN.
So, in your case, arguments[0] will be the first argument that getAjax() passes to the callback you passed into it as the last argument when you called getAjax(). So, it depends upon the internal behavior of getAjax() which you do not show us.
Let's walk through how your code works:
You make a function call to getAjax() and pass it a number of arguments.
One of those arguments is a callback function (the last argument).
When getAjax() is doing its job, it will, at some point, call that callback function.
When it calls that callback function, it can pass that callback some arguments.
Within that callback function the arguments object will represent whatever arguments were passed to it by getAjax().
So, when (in that callback function), you then get arguments[0] and pass it to show_list(), you will be passing whatever the first argument was that getAjax() passed to your callback.
You could rewrite your code without using the arguments object like this by just declaring a named argument for your callback:
getAjax('get_info', 'java_array', realVarName, cld.listArray, 0, '',
'no_restrict', function(obj) {
show_list(obj, sld);
if (typeof(postFunc) == "function") {
postFunc();
}
});
In this alternate implementation, you name the first argument to the callback function to be obj and use it directly rather than using arugments[0]. Both your implementation and this produce the same result.
It is generally better to use named arguments (like the obj in my alternate implementation) when you know what arguments are going to be passed to a function as this makes the code more self documenting. But the arguments object can be particularly useful when you don't know how many arguments are going to be passed to a function or when you want to pass whatever arguments were passed to your function to some other function (forwarding or proxying).
This question already has answers here:
Applying a Function to Null in Javascript
(5 answers)
Closed 7 years ago.
I am learning about call and apply in javaScript from a online TUT. This function allows more arguments to be passed, rather than having a fixed amount.
var calculate = function(){
var fn = Array.prototype.pop.apply(arguments);
return fn.apply(null, arguments);
};
What I am having difficulty wrapping my head around is this statement.
var fn = Array.prototype.pop.apply(arguments);
The presenter of the of the TUT, explained it as the following:
We are binding the apply method onto the arguments object. This is going to give us the Function Object and assign it to the fn variable. It will also remove the Function Object from the argumentsObject. Because the Array's pop method takes the final element in the array, it removes it from the Array and then assigns to what ever called the method. In this case the fn variable.
What confused me was the following:
We are binding the apply method onto the arguments object. This is
going to give us the Function Object
It will also remove the Function Object from the arguments
Object.
And when we write in the return statement:
return fn.apply(null, arguments);
Why are we including null?
Array.prototype.pop.apply(arguments);
When you have a function, there's automatically an arguments objects, which is an Array-like object of arguments. If you call this fake function:
someFunction('hello', 'world');
and someFunction looks like this:
function someFunction() {
console.log(arguments);
}
The console.log will output ['hello', 'world']. However, don't be confused... That is not an Array object! It is an "array-like" object. Therefore, you can't say arguments.pop()... because arguments doesn't have that method (it belongs to Array.prototype). However, normal Array objects do have access to Array.prototype (e.g. [1,2,3].pop() // => [1,2]).
When you say .apply(), the first argument is the context... It sets the this. So really, Array.prototype.pop.apply(arguments) is a clever way of mimicking arguments.pop(). But you can't do arguments.pop(), because it doesn't have a pop method.
In return fn.apply(null, arguments);, null is the first arguments because we don't need to set a new context for this example. arguments is the second arguments because it's being passed in to use with fn.
.apply() returns a function object, so it returns something like this:
function() { ... }
We can then later invoke that function.
By the way, .pop() mutates the original object (in this case, the array-like object arguments). So you're passing in arguments to fn, but it's missing the last item that was in it previously.
According to MDN:
Syntax
fun.apply(thisArg, [argsArray])
Parameters
thisArg:
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray:
An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
The last argument passed to calculate is assumed to be a function. It is popped from the arguments list. (Using apply because arguments is not a real array.)
This popped function (fn) is called with the rest of the arguments list. (All other arguments passed to calculate). The arguments list no longer contains fn because pop() modifies the original object.
NULL is used because fn is called without a value for this. (See MDN)
If you call calculate for instance like
calculate(2, 3, function(a, b){ return a + b });
it will return 5.
This question already has answers here:
Why should use "apply"?
(5 answers)
Closed 8 years ago.
when read the heatmap code, I find a code like
this._store.addData.apply(this._store, arguments);
I am very confused about this usage of 'apply'.Is there any different in the code below
this._store.addData(arguments);
thanks!
The point of using .apply here is that apparently the function arguments are in the array arguments. The equivalent code without .apply would be:
this._store.addData(arguments[0], arguments[1], ..)
Which is obviously troublesome if arguments is of unknown length.
Passing this._store again is only a necessity, it's not the point of this code.
The two are not the same.
.apply() as documented here on MDN, sets the value of this when the function is executed and passes an array of arguments to that function which will be expanded into normal function arguments.
Your second code example passes an array-like object of arguments to the actual function rather than an expanded list of arguments so they are not the same.
As an example, if there are three items in the arguments pseudo-array, then the first option will call your function like this:
this._store.addData(arg1, arg2, arg3);
And, your second code block will call it like this:
this._store.addData([arg1, arg2, arg3]);
Though technically, it's not even passing an array, it's passing a pseudo-array that doesn't actually have the array methods.
The reason for using .apply() is when you have a list of arguments in an array or array-like structure and you want to call a function using that list of arguments as the arguments to the function (passed to the function as normal arguments). It is very commonly used when you want to just forward the arguments from one function to the next or when you want to modify the arguments (e.g. remove one or change one) and then call another function with all the rest of the arguments.
Or sometimes .apply() is used only to control the this pointer and no arguments are passed (though .call() can also do that).
As a corollary, .call() is used when you know exactly how many arguments there are (e.g. you know there are two arguments AND you want to control the this pointer or .call() is often used when you just want to control the this pointer and don't have any arguments. An advantage of .apply() is that you don't have to know how many arguments there are - you can just pass on whatever arguments there were (that's why it's particularly useful when forward the args from one function to the next) like when calling a base class constructor or when proxying for a function like what .bind() does.
This this pointer will be the same in the two methods because calling obj.method() will set the value of this to obj which in both of your cases is this._store.
Note, that these two would be the same:
this._store.addData(arguments);
this._store.addData.call(this._store, arguments);
I’d like to know both for regular all-in-the-family JS developer-defined functions, as well as predefined DOM methods: what happens if I try to call IE’s attachEvent with the signature of the WHATWG’s addEventListener? For instance:
elem.attachEvent('onbillgates\'mom', function(e){ this.mount(); }, false);
Specifically, note the third argument false. Will that trip anything up, even though the attachEvent method’s signature only calls for two arguments?
What about this example?
function foo(FirstOf2, SecondOf2) {
console.log(FirstOf2 + SecondOf2);
}
foo(1, 2, true);
JavaScript doesn't have the concept of a fixed parameter list. For your own functions you can always specify as many parameters as you want and pass in as many as you want which ever type you want.
For built-in functions, which correlate to native code, it depends.
You asked on what it depends:
Let's look at the ECMA-262
Section 15 about built-in (not to confuse with host) functions in general
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given fewer arguments than the function is specified to require, the function or constructor shall behave exactly as if it had been given sufficient additional arguments, each such argument being the undefined value.
Alright. If I pass in less arguments than needed, it depends on the spec of the function itself (scroll down section 15 to find the spec for each built-in function).
Unless otherwise specified in the description of a particular function, if a function or constructor described in this clause is given more arguments than the function is specified to allow, the extra arguments are evaluated by the call and then ignored by the function. However, an implementation may define implementation specific behaviour relating to such arguments as long as the behaviour is not the throwing of a TypeError exception that is predicated simply on the presence of an extra argument.
Passing in too many arguments should never raise a TypeError. But still it may raise other errors. Again, it depends on the function you talk about.
You were talking explicitly about the DOM and not about built-in functions. To be honest I can't find the corresponding parts of the spec. The ECMA spec is so much easier to read then the w3 website.
Won't hurt. You can even call a function with less parameters than it takes, as long as the function code is ok with a few undefined values.
I came across this important, however old, question; and I hope it'll be beneficial for future generations to share my experiments with it:
One can use the arguments object in order to access a function's arguments, regardless of the amount of arguments in the function's signature.
It's worth mentioning that this doesn't apply to arrow functions:
function singleArg(x) {
console.log(arguments);
}
singleArg(1, 2); // Called with 2 arguments
singleArg(); // Called with 0 arguments
// Results in an error, as 'arguments' isn't defined for arrow functions
((arg) => console.log(arguments))(1);
It's stated in the documentation that arguments isn't exactly an Array:
“Array-like” means that arguments has a length property and properties indexed from zero, but it doesn't have Array's built-in methods like forEach() and map().
Hence the following code results in an error:
(function singleArg(x) {
console.log(arguments); // This line works
arguments.forEach(x => console.log(x)); // This causes an error
})(1, 2);
Upon calling a function with less arguments than in its signature, they're assigned undefined:
(function twoArgs(a, b) {
console.log(`a: ${a}\nb: ${b}`);
})(1);
Try taking a look at this post and perhaps this one.
From MDN:
The arguments object is a local variable available within all
functions; arguments as a property of Function can no longer be used.
You can refer to a function's arguments within the function by using
the arguments object. This object contains an entry for each argument
passed to the function, the first entry's index starting at 0.
You can use the arguments object if you call a function with more
arguments than it is formally declared to accept. This technique is
useful for functions that can be passed a variable number of
arguments.
function myConcat(separator) {
var result = "";
// iterate through non-separator arguments
for (var i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
I don't think it will mess anything up unless you are explicitly dealing with the implicit arguments array. Why are you doing this though?