StickyCube
StickyCube

Reputation: 1721

Why does the javascript arguments object behave so strangely when you try to modify it?

consider the following:

foo intends to take the arguments object and rearrange the order, moving arg1 to the position of arg2

function foo (args) {
    args[2] = args[1];
    args[1] = undefined;
}

bar calls foo with it's arguments

function bar (a, b, c) {
    foo(arguments);
    console.log(arguments);
}

I expect the result of the following to be something like { 0: 'hello', 1: undefined, 2: 'world' }

bar('hello', 'world');

However, i get:

{
    0: 'hello',
    1: undefined,
    2: 'world',
    3: undefined,
    4: undefined,
    5: undefined,
    6: undefined,
    7: undefined,
    8: undefined,
    9: undefined,
    10: undefined,
    11: undefined,
    12: undefined,
    13: undefined,
    14: undefined,
    15: undefined,
    16: undefined,
    17: undefined,
    18: undefined,
    19: undefined
}

I am at a complete loss as to why this happens. Anyone have any ideas?

I'm running this in a node.js environment

Upvotes: 6

Views: 98

Answers (1)

Alan Tam
Alan Tam

Reputation: 2057

The arguments object is not an array. It is a list with the internal type Arguments, with a length property and getters/setters for properties 0 to len - 1 where len is the lesser of the number of declared arguments of the function and the number of elements you called the function. Once this object is created, the system will not increase/decrease length when you operate on its properties, and attempting to set it a length does not add/remove keys. The getters/setters for properties 0 to len - 1 are indeed aliases to your argument names within the function (i.e., when you set b = 1 you will see arguments[1] === 1).

What's happened when foo tries to set args[2] is addition of an integral property triggers V8 to resize the underlying array storage to 20 elements (it somehow should know that this is an Arguments type so it can probably set it to a hash property instead). If you set args[20] = 1 it will be resized to 47, args[100] = 1 will resize to 167, but setting args[1026] = 1 will make it a sparse array (but first setting args[1025] = 1 and then args[2048] does not make it sparse), etc.

After resized, Object.keys reports all 0 to 19 as properties, so console.log (which calls util.format) just prints all of them.

(function (a, b, c) { (function (args) { args[2] = 1; })(arguments); console.log(Object.keys(arguments)); })("hello", "world")

["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"]

This is definitely a V8 bug, although it is very edgy.

Upvotes: 3

Related Questions