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).
Related
I'm trying to understand the below code which prevents bugs caused by a function silently passing to a call back function more arguments that aren't wanted in the parameters of the callback function:
["1","2","3"].map(parseInt)
returns [1, NaN, NaN] because the index gets passed as a second parameter to parseInt which accepts it as the radix, so the array values are invalid. To solve it, the author of this blog post suggests a js extension:
Function.prototype.only = function(numberOfArgs) {
var self = this; //the original function
return function() {
return self.apply(this,[].slice.call(arguments,0,numberOfArgs))
}
};
Which would be called like so:
["1","2","3"].map(parseInt.only(1))
I'm struggling to understand why "this" is passed as a function parameter while self is returned as a reference to this. They both point to parseInt correct? Is there a difference in binding times of parameters vs return values?
They both point to parseInt correct?
No.
The value of this depends on how a function is called.
The function passed to map gets called by the internals of map.
Look at the documentation for map.
If a thisArg parameter is provided to map, it will be used as callback's this value. Otherwise, the value undefined will be used as its this value.
So if the returned anonymous function used this instead of self it would be calling undefined.apply not parseInt.apply.
I'm struggling to understand why "this" is passed as a function parameter while self is passed as a reference to this. They both point to parseInt correct?
No. self will refer to parseInt. this will be whatever this the function was called with, which could be anything. In your example:
["1","2","3"].map(parseInt.only(1))
...this will either be a reference to the global object (in loose mode) or undefined (in strict mode), because map will use undefined when calling the callback (which is turned into a reference to the global object in loose mode).
There are several ways this might be something else though:
If a second argument were passed to map:
["1","2","3"].map(parseInt.only(1), {})
// Second argument -----------------^^
...it would use that value rather than undefined when calling the callback.
If Function#bind were used on the function only returns:
["1","2","3"].map(parseInt.only(1).bind({}))
// Bind ---------------------------^
...then this would be that value.
If the function only returns were called with call or apply (which is what map does, so this is kind of a repetition of #1 :-) ), the value supplied for this would be used.
isInSet requires two arguments, but binds passes only theSet as first argument. I am not able to figure out how bind method works in this case
function isInSet(set , person) {
return set.indexOf(person.name) > -1;//checks for existence of person.name in theSet
}
console.log(ancestry .filter(function(person) {
return isInSet( theSet , person) ;
}) ) ;
// ! [{ name : " Maria van B r u s s e l " , ...} ,
// { name : " Carel H a v e r b e k e " , ...}]
console.log(ancestry.filter(isInSet.bind(null, theSet))) ;//**how does this work?**
// !... same result
bind() creates a new function from the function that it is called on. It sets the this keyword in the newly created function to be the first argument that you pass it (if null is passed, then it does not overwrite the default this keyword). You can also pass in extra arguments to bind() and if you do, they will always be inserted into the new function. So for instance, let's say you have a sum function that takes 2 arguments.
function sum (a, b) {
return a + b;
}
Now we could create a new function from this using bind() and always pass in one argument.
var boundSum = sum.bind(null, 2);
This will always bind 2 as the first argument in the sum() function. Now anytime you call that boundSum() function, it will only take one argument, as the 2 is already bound.
boundSum(3); // <-- this would return 5
Your example is using a similar principle. Because you are calling isInSet.bind(null, theSet) it is always binding theSet to the first parameter passed in the isInSet function. However, it is still missing the second parameter. The reason why it works is because you are putting that inside of an ancestry.filter() function. filter() essentially loops over an array and passes each element to the function inside of it (check the docs). So therefore, each element in the ancestry array is getting passed to that bound function, which makes it the second parameter of isInSet().
Read the text from the book a few times and compare the unbound and the bound version.
Calling .bind() on a function, return a new function, with some of the arguments already filled in. The isInSet function, expects two parameters: the set to filter, and a person having a name.
The .filter() method of an array, expects one parameter, namely the function to which each element of the array has to be sent to.
So when you look to the unbound version, you see that the filter function used, just returns isInSet(theSet, person).
So to make the two compatible, we .bind() to create a new function with only one parameter left (namely 'person'), that is bound to theSet. So every time this new function returned by bind gets called, it will use theSet as it's first parameter and will expect only one parameter, 'person'.
Here, person, the second parameter of the original isInSet function we didn't bind, is used as the first and only parameter of the bound function.
So we have exactly what we need after using the bind. A function that will take a 'person' as a parameters and will always look inside the same array (theSet).
If you read the docs for .bind(), you see that it accepts a variable list of arguments. The first argument is the 'this' argument. So if you have a function that uses 'this' inside it and you want to bind it to eg. an object, you can use the 1st parameter. Eg. myFunc.bind( objectToCallUpon, firstArg, secondArg );
Since we don't actually use the 'this' argument in the isInSet() function, we just pass 'null' as the 'this' value.
All the other parameters used are the arguments of isInSet you want to make 'fixed'. Since we want the first parameter to always be theSet, we bind that value.
If the isInSet function would accept two parameters, you would use:
isInSet.bind( null, theSet, secondParameter );
Mozilla's Developer Network states:
[the forEach callback] is invoked with three arguments:
the element value
the element index
the array being traversed
However, this is a little unclear to me. Are all three of these arguments necessary? The specific callback I'm writing will not use any of those values. Do I declare them anyway, or can I safely skip them?
You can safely skip them.
You never HAVE to declare function arguments. Often, values will be passed to callbacks whether or not you've prepared the callbacks to receive them - it matters only if your function needs to make use of those arguments.
An example is any event callback, e.g., to which the event object - reporting about what happened - is always passed as the first argument.
foo.addEventListener('click', function(evt) {
//if I never reference the evt object, I need not define the evt argument
}, false);
Unlike most of the languages, javascript doesn't perform any check on the number of arguments with which a function is called. Since javascript callbacks are nothing but functions, this holds true for callbacks also.
function a(arg1){
console.log(arg1);
}
function a can be invoked by
a(); // prints undefined
a(1); // prints 1
a(1,2); // prints 1
or with any number of arguments.
If a function is invoke with lesser number of argument than in the definition, the missing parameters are set to undefined.
All the arguments with which a function is called are available as a local variable arguments
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.
I'm trying to pass the arguments variable of a function as separate arguments to another function. I tried this:
function call_function(func) {
func(arguments);
}
However, call_function(someFunction, 123, 456) actually calls someFunction([someFunction, 123, 456]). Regardless of the function itself being passed, it is passed as an Arguments object/array-like thing, but I'd rather call the function in this case as someFunction(123, 456).
What I also tried is: func(Array.prototype.slice.call(arguments, 1)) to remove the function (first argument), but obviously this actually returns an array, i.e. it passes the array as the first argument to the function.
How could I code call_function so that a call like call_function(someFunction, 123, 456) results in someFunction(123, 456) being called?
You can use the apply function to call a function with the array as arguments.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply