36

I am learning JavaScript and am pretty confused about the arguments property array.

I have a function that takes a single argument and returns it. When I pass the parameter and reassign it using arguments[0] = value, it's updating the value.

function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(1)); //returns 2

But when I call the same function with no parameters it returns undefined.

function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a()); //returns undefined

But even if I pass undefined, the value will update as well.

function a(b) {
  arguments[0] = 2;
  return b;
}
console.log(a(undefined)); //returns 2

I thought that if you do not pass a parameter to a JavaScript function, it automatically creates it and assigns the value to undefined and after updating it should reflect the updated value, right?

Also a() and a(undefined) are the same thing, right?

grooveplex
  • 2,492
  • 4
  • 28
  • 30
A Rogue Otaku
  • 913
  • 10
  • 20

6 Answers6

24

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

  1. Let len be the number of elements in args.

  2. Let indx = len - 1.

  3. 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:)

    1. Add name as an element of the list mappedNames.
    1. Let g be the result of calling the MakeArgGetter abstract operation with arguments name and env.
    1. Let p be the result of calling the MakeArgSetter abstract operation with arguments name and env.
    1. 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
Community
  • 1
  • 1
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • So if I understand it correctly then if I do not pass any parameters then the arguments object will have length 0. So if I try to access that length 0 object with arguments[0] should it not throw an error? Or is it like there IS a index 0 but just no setter and getter? – A Rogue Otaku Jan 23 '19 at 09:30
  • 2
    The `arguments` object is defined regardless, and nearly any object can have any property assigned to it at will. For example, `const foo = {}; foo[0] = 'bar';`. That's kind of similar to your `arguments[0] = 2` when `a` is being called with no arguments - there's no setter or getter on index 0, so you can assign to it without problems, it won't throw an error, but it won't affect anything else. – CertainPerformance Jan 23 '19 at 09:36
  • 2
    I see. Thanks. Wow JavaScript is weird. – A Rogue Otaku Jan 23 '19 at 09:39
  • Nice, haven't seen an answer cite the ES5 spec in ages :-) It's totally appropriate here, though. – Bergi Jan 23 '19 at 19:06
  • @AmitDas Always [`"use strict"` mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode), and JavaScript becomes a lot less weird. – Bergi Jan 23 '19 at 19:07
7

ECMA 262 9.0 2018 describes this behaviour in 9.4.4 Arguments Exotic Objects with

NOTE 1:

The integer-indexed data properties of an arguments exotic object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function's execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. If the arguments object is an ordinary object, the values of its properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.

In short,

  • if in 'sloppy mode', then all arguments are mapped to their named variables, if the length correspond to the given parameter, or

  • if in 'strict mode', then the binding is lost after handing over the arguments.

This is only readable in an older version of ECMA 262 7.0 2016. It describes this behaviour in 9.4.4 Arguments Exotic Objects with

Note 1:

For non-strict functions the integer indexed data properties of an arguments object whose numeric name values are less than the number of formal parameters of the corresponding function object initially share their values with the corresponding argument bindings in the function's execution context. This means that changing the property changes the corresponding value of the argument binding and vice-versa. This correspondence is broken if such a property is deleted and then redefined or if the property is changed into an accessor property. For strict mode functions, the values of the arguments object's properties are simply a copy of the arguments passed to the function and there is no dynamic linkage between the property values and the formal parameter values.

Community
  • 1
  • 1
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

it's because arguments it's not like a Array, it's a object with integer indexed data keys, and property length, And if length equal zero it's mean you don't have a arguments

function a(b) {
    arguments[0] = 2;
    console.log(arguments.length) 
    return b;
}
a(1); // length 1  returns 2
console.log(a());  // length 0  returns undefined
Vadim Hulevich
  • 1,803
  • 8
  • 17
1

My understanding is that the arguments object only tracks what is passed into the function. Since you've not initially passed anything, b is not bound and at that point arguments is not 'tracking' b. Next, you assign a value to the initialised but empty Array-like object arguments and finally return b, which is undefined.

To delve into this further:

If a non-strict function does not contain rest, default, or destructured parameters, then the values in the arguments object do change in sync with the values of the argument variables. See the code below:

function func(a) { 
  arguments[0] = 99; // updating arguments[0] also updates a
  console.log(a);
}
func(10); // 99

and

function func(a) { 
  a = 99; // updating a also updates arguments[0]
  console.log(arguments[0]);
}
func(10); // 99

When a non-strict function does contain rest, default, or destructured parameters, then the values in the arguments object do not track the values of the arguments. Instead, they reflect the arguments provided when the function was called:

function func(a = 55) { 
  arguments[0] = 99; // updating arguments[0] does not also update a
  console.log(a);
}
func(10); // 10

and

function func(a = 55) { 
  a = 99; // updating a does not also update arguments[0]
  console.log(arguments[0]);
}
func(10); // 10

and

// An untracked default parameter
function func(a = 55) { 
  console.log(arguments[0]);
}
func(); // undefined

Source: MDN Web docs

Barzev
  • 402
  • 3
  • 14
1

When you are not providing any parameter then arguments array has length equal to 0. Then you are trying to set the non existing element of array to 2 which causes returning undefined

You can simply test this with this snippet:

function a(b){ 
    alert(arguments.length) // It will prompt 0 when calling a() and 1 when calling a(undefined)
    arguments[0] = 2; 
    return b; 
}
puffy.bun
  • 256
  • 1
  • 10
1

This is the undefined value definition from javascript spec :

primitive value used when a variable has not been assigned a value.

so if you do not specify the function return type it will return undefined.

so a() and a(undefined) it is not same thing. returning undefined is based on return type is defined or not.

for more clarification similar_problem

sathish kumar
  • 1,477
  • 1
  • 11
  • 18