Reputation: 2048
I have a function with two simple loops which handle function's arguments:
var getArgs=function(){
var keys = Object.keys(arguments);
for(var argKey in arguments){
console.log({argKey:argKey, argument:arguments[argKey]});
}
keys.forEach(function(argKey){
console.log({argKey:argKey, argument:arguments[argKey]});
});
};
...and its call:
getArgs(5,3,2,11,15,7,-25);
It seems that they do the same, so the output should be the same also, but it is not. The first one works quite predictable. The output is:
{ argKey: '0', argument: 5 }
{ argKey: '1', argument: 3 }
{ argKey: '2', argument: 2 }
{ argKey: '3', argument: 11 }
{ argKey: '4', argument: 15 }
{ argKey: '5', argument: 7 }
{ argKey: '6', argument: -25 }
But the second one behaves unexpected:
{ argKey: '0', argument: '0' }
{ argKey: '1', argument: 1 }
{ argKey: '2', argument: [ '0', '1', '2', '3', '4', '5', '6' ] }
{ argKey: '3', argument: undefined }
{ argKey: '4', argument: undefined }
{ argKey: '5', argument: undefined }
{ argKey: '6', argument: undefined }
Why?
Upvotes: 2
Views: 56
Reputation: 1048
The reason why the foreach doesn't work is you are taken the arguments into a closure, so you are taken the arguments from the arguments.
Try to add a console.log(arguments) into the foreach closure and you can see what it happens.
I provide some examples that works and it is based in your code.
var getArgs=function(){
var keys = Object.keys(arguments);
var myargs = arguments; // Copy arguments object (not as reference) so we can keep it into the scope when the arguments are reading from a closure or callback.
console.log("var in method");
for(var argKey in arguments){
console.log({argKey:argKey, argument:arguments[argKey]});
}
console.log("foreach method by key");
keys.forEach(function(value, key) {
console.log({argKey:key, argument:myargs[key]});
});
// Because we don't have foreach method for objects we can slice it as an array
console.log("foreach method by argument array");
Array.prototype.slice.call(arguments).forEach(function(value, key) {
console.log({argKey:key, argument:value});
});
};
getArgs(5,3,2,11,15,7,-25);
Upvotes: 1
Reputation: 3593
wich arguments
-object? you're juggling with two-ish different arguments
-objects.
it's like the this
-keyword, context matters!
take a look at this:
var getArgs = function(){
var keys = Object.keys(arguments);
for(var argKey in arguments){
console.log({argKey:argKey, argument:arguments[argKey]});
}
var _args = arguments;
keys.forEach(function(argKey){
console.log({
argKey: argKey,
argument: arguments[argKey],
argumentFunction: arguments.callee.toString(),
realArgument: _args[argKey],
realArgumentFunction: _args.callee.toString()
});
})
}
//or that:
var getArgs = function(){
var keys = Object.keys(arguments);
keys.forEach((argKey) => {
console.log({
argKey: argKey,
argument: arguments[argKey]
});
})
}
but as soon as you are messing around with the arguments-object (and passing it to some function like Object.keys()
counts as messing around) this function can't be optimized anymore.
var getArgs = function(){
//you don't need for..in, you are not iterating over an Object or a (huge) sparse Array
for(var i=0, len=arguments.length; i<len; ++i){
console.log({
argKey:i,
argument:arguments[i]
});
}
}
If you want to pass the arguments around, you should copy them first to an Array.
for(var i = arguments.length, args = new Array(i); i--; ) args[i] = arguments[i];
//now you can do whatever you want to args.
but what's the purpose of this. such dynamic argument-lengths are rarely a good idea, if you want to pass a list, then pass a list (aka Array or sth like that)
Upvotes: 1
Reputation: 4346
The for loop is not a function. As such, arguments
retains the context of getArgs
.
The forEach
while you are thinking of it as a loop, is actually a function that runs a loop. As such, arguments
has a new context which is not what you are expecting.
To solve this, set arguments to a new variable and use that variable in the forEach
.
Run this code and you will get your expected result:
var getArgs=function(){
var keys = Object.keys(arguments);
var args = arguments;
for(var argKey in arguments){
console.log({argKey:argKey, argument:arguments[argKey]});
}
keys.forEach(function(argKey){
console.log({argKey:argKey, argument:args[argKey]});
});
};
Upvotes: 2