3

I think it is an old Javascript behavior (Crockford said it is a design error) that inside a function, arguments is like an array, except it is not a real array, so array methods cannot be invoked on it:

function foo() { console.log(arguments.slice(1)) }   // won't work
foo(1,2,3);

And I just tried it on the latest Firefox and Chrome, and it won't work on both. So we may have to use

function foo() { console.log(Array.prototype.slice.call(arguments, 1)) }
foo(1,2,3);

But why not make arguments a real array in the modern JavaScript? There probably shouldn't be any program that depends on arguments not being a real array? What might be a reason not to make it a real array now?

One reason I can think of is, if programmers start treating it as an array, then the code won't work in older browsers, but there are other things in ECMA-5 that won't work in older browsers too.

nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • 2
    The Array object comes with a lot of baggage. There are a number of properties and methods that are part of the Array object. This is likely an optimization for speed, since arguments is a part of every method. – Shmiddty Nov 01 '12 at 20:17
  • 3
    The `arguments` indexed values have to function as aliases for the parameters, and it would be pretty difficult to do that with a plain array. – Pointy Nov 01 '12 at 20:20
  • is the optimization that important? For one thing, those properties are just prototypal inherited, so what matters is only the `__proto__` and even if we use those inherited array properties, how heavy can it be? – nonopolarity Nov 01 '12 at 20:22
  • Also it's so trivial to use `slice()` etc. with `arguments` that there's really no point; there are other more serious problems with the language that are better targets for improvement. – Pointy Nov 01 '12 at 20:27
  • @Shmiddty: Why would it matter if there are other methods on an Array? How would that affect performance? – I Hate Lazy Nov 01 '12 at 20:29
  • 1
    Also, something's going to have to provide the "callee" property (evil as it may be) or else lots of stuff is going to break. – Pointy Nov 01 '12 at 20:30
  • @Pointy since primitive types are not mutable, and objects can be just by copying a reference, so can't the copying of arguments into this array be quite lightweight? – nonopolarity Nov 01 '12 at 20:32
  • 2
    @動靜能量 the relationship between `arguments[0]` and the first named parameter is surprising. If you change `arguments[0]`, the value of the first named parameter changes, and vice-versa. It's the only situation in JavaScript where there is such aliasing. The `arguments` object cannot just be a **copy** of the parameters: it has to **be** the parameters. – Pointy Nov 01 '12 at 20:34
  • 1
    @Pointy: Except in strict mode, where they are independent. – I Hate Lazy Nov 01 '12 at 20:35
  • @user1689607 well sure, in strict mode that's true, but that's not what was asked (unless "modern JavaScript" really means "strict mode"). – Pointy Nov 01 '12 at 20:36
  • @Pointy: Just pointing out that exception since strict mode is *part* of modern JavaScript. – I Hate Lazy Nov 01 '12 at 20:40
  • @user1689607 OK fair enough :-) – Pointy Nov 01 '12 at 20:41

5 Answers5

4

Until very late in the development of ECMAScript 5, argument object were going to inherit all of the Array.prototype methods. But the "final draft" of ES5 approved by TC39 in Sept. 2009 did not have this feature.

In August 2009, Oliver Hunt of Apple posted this to the the es5-discuss mailing list

https://mail.mozilla.org/pipermail/es5-discuss/2009-August/003112.html

I implement the logic to make the Arguments object inherit from Array in WebKit last friday and it's quickly turned up a severe
incompatibility with Prototype.js ... This breaks at the very least a number of Apple sites and Nasa.gov -- ... Due to these site breakages, caused by a major compatibility problem
in a fairly major library it seems infeasible to attempt to retrofit
array like behaviour onto arguments.

The rest of TC39 agreed with Oliver's assessment and the feature was removed from the final draft.

Perhaps, such code has disappeared sufficiently from the web that the proposed ES5 solution would work today. However, it doesn't matter because rest parameters in ES6 is a better solution to the problem and a completely new syntactic feature that can't have any backwards compatibility issues with existing code.

Allen Wirfs-Brock
  • 1,094
  • 1
  • 8
  • 10
  • Chris Pine had mentioned some incompatibilities back in ES4 days (from the parts that Linear B and later Futhark exp. implemented), from what I can tell of mentions in the bug tracker (don't have minutes, Jan '07, etc.) but obviously it was decided to stick with it them. Prototype.js was the first major breakage we saw, too, so it obviously hadn't changed in three years (well, except for special-casing Opera's "weird" behaviour). – gsnedders Nov 03 '12 at 22:13
1

There are sites online that rely upon arguments not being an array, such as those using older versions of Prototype and script.aculo.us. This means that any browser that changed it (ES4 included this, and it was implemented along with numerous other parts in Futhark, used in Opera from 9.5–10.10) would break these sites, and there's a strong market encouragement to not break sites (any browser that breaks websites will not get used by users who care about those sites for obvious reasons, given many sites are rarely updated).

gsnedders
  • 5,532
  • 2
  • 30
  • 41
  • In what way are they relying on it? – Bergi Nov 01 '12 at 20:55
  • @Bergi All sorts of ways; a fairly trivial one is `print(arguments.length); arguments[arguments.length] = 1; print(arguments.length);`. As an Array, the assignment would mutate the length property; otherwise, it does not. – gsnedders Nov 01 '12 at 21:02
  • Yes, that's true. Yet where in its code does Prototype add elements to an `Arguments` object? – Bergi Nov 01 '12 at 21:05
  • @Bergi If you look at Prototype 1.5, `Array.from`/`$A` forks on `iterable.toArray` — the `arguments` object wouldn't have this unless it has the Array prototype object on its prototype chain — causing it to return `[].concat(this)`. In browsers apart from Firefox (and maybe IE, untested) this is the Arguments object. If you then concatenate a non-empty array to that, it has a different length to what you might expect. So, e.g., their old Function.prototype.bind impl breaks. – gsnedders Nov 01 '12 at 21:24
  • https://mail.mozilla.org/pipermail/es-discuss/2008-September/007405.html has more discussion about breakage. – gsnedders Nov 02 '12 at 18:01
  • Thank's for that link, the email contains lots of good arguments. – Bergi Nov 03 '12 at 16:30
0

In the next version of ECMAScript, this issue (and several others) is being addressed with rest parameters.

function foo(...rest) {
    console.log(rest.slice(1))
}

foo(1, 2, 3);

Unlike arguments, rest parameters will be real arrays, so this will work.

Rest parameters can do even more. In the above example you probably wanted to use the first argument for one thing and everything after it for something else. You could do this instead:

function foo(first, ...rest) {
    console.log('first: ', first);
    console.log('rest: ', rest);
}

foo(1, 2, 3);

This will log:

first: 1

rest: [ 2, 3 ]

The proposal: http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters

Nathan Wall
  • 10,530
  • 4
  • 24
  • 47
  • `arguments` is never going. There is no "opt-in stricter version" for ES6. – gsnedders Nov 01 '12 at 20:55
  • Ah, it was the fat arrow functions where I had read that `arguments` may not exist. – Nathan Wall Nov 01 '12 at 21:06
  • Also, under **Goals** for [rest parameters on the Harmony page](http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters): "Provide a better `arguments` so we can deprecate, obsolete, and some years hence burn with fire / salt the earth anything to do with arguments, foo.arguments, etc." – Nathan Wall Nov 01 '12 at 21:09
  • I'll believe it when I see it, personally. (Various people in the TC have been trying to kill `foo.arguments` for years, and this still hasn't happened, and given its continuing ubiquity, I don't see even that happening, yet alone arguments more generally.) – gsnedders Nov 01 '12 at 21:27
0

It is mainly because it needs to be read-only as far as I can deduct.

If it were an array, then it needs to be a read-only array which means that we have to get rid of push, pop, splice etc... any method that modifies the array. By this point, even though I agree other methods like slice might come handy but it's already a data structure that has different requirements than javascript Array.

I think they shouldn't have said it is an array-like object, in my view, it is just a different object that happened to have a property called length (same as Array).

kabaros
  • 5,083
  • 2
  • 22
  • 35
  • Calling it an array-like object basically means just that: it has a `length` property. It means that you can use it with array methods, such as `slice`, `forEach`, `map`, etc. Also, it's not read only. There's nothing to stop you from using `push` on it. `function foo() { Array.prototype.push.call(arguments, 'new'); console.log(arguments); } foo(1, 2, 3);` – Nathan Wall Nov 01 '12 at 20:47
  • Yes, I know it can be modified. But what I meant that from a design point of view, the designers of the language - probably - wanted arguments to be a read-only structure of the functions' arguments, that's what I would expect from a property that returns a function's list of arguments. That wouldn't stop you from going out of your way to change the list, but that shouldn't be the default behaviour. – kabaros Nov 01 '12 at 20:55
0

what prevents modern implementation not to treat arguments as a real array?

Implementations of what? The EcmaScript 5.1 specification, yes. Yet there is quite accurate specified that there should be an arguments binding and what such an Arguments object is.

Also, an Arguments object just is not a real Array, as it has some very special behaviour regarding its properties. [[Get]], [[Delete]] etc are overwritten to reflect the function argument variables. Calling push, splice etc on such an object if it were an array could cause havoc.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375