Assigning to arguments indicies will only change the associated argument value (let's call it the n-th argument) if the function was called with at least n arguments. The arguments object's numeric-indexed properties are essentially setters (and getters):
http://es5.github.io/#x10.6
Italics in the below are my comments on how the process relates to the question:
(Let) args (be) the actual arguments passed to the [[Call]] internal method
Let len be the number of elements in args.
Let indx = len - 1.
Repeat while indx >= 0, (so, the below loop will not run when no arguments are passed to the function:)
(assign to the arguments object being created, here called map:)
-
- Add
name as an element of the list mappedNames.
-
- Let
g be the result of calling the MakeArgGetter abstract operation with arguments name and env.
-
- Let
p be the result of calling the MakeArgSetter abstract operation with arguments name and env.
-
- Call the [[DefineOwnProperty]] internal method of
map passing ToString(indx), the Property Descriptor {[[Set]]: p, [[Get]]: g, [[Configurable]]: true}, and false as arguments.
So, if the function is invoked with no arguments, there will not be a setter on arguments[0], so reassigning it won't change the parameter at index 0.
The same sort of thing occurs for other indicies as well - if you invoke a function with 1 parameter, but the function accepts two parameters, assigning to arguments[1] will not change the second parameter, because arguments[1] does not have a setter:
function fn(a, b) {
arguments[1] = 'bar';
console.log(b);
}
fn('foo');
So
a() and a(undefined) are the same thing right?
is not the case, because the second results in an arguments object with a setter and a getter on index 0, while the first doesn't.
Note that this odd interaction between the arguments and the function parameters is only present in sloppy mode. In strict mode, changes to arguments won't have any effect on the value an individual argument identifier contains:
'use strict';
function a(b) {
arguments[0] = 2;
return b;
}
console.log(a(1)); //returns 1