2

While answering this question ( Difference between [Object, Object] and Array(2) ) I happened across something in JavaScript arrays that I wasn't previously aware of (ironic, considering I worked on Arrays in the Chakra engine while at Microsoft).

If I enter this into Chrome's JavaScript console...

var x = Array(2);
var y = [undefined, undefined];
var z = [,];

...then when query the x, y and z variables I get this output respectively:

> x
< [undefined × 2]
<    length: 2

> y
< [undefined, undefined] (
<    0: undefined
<    1: undefined
<    length: 2

> z
< [undefined × 1]
<    length: 1

MDN's relevant documentation for Array(arrayLength) and the [] literal syntax states:

If the only argument passed to the Array constructor is an integer this returns a new JavaScript array with its length property set to that number (Note: this implies an array of arrayLength empty slots, not slots with actual undefined values). If the argument is any other number, a RangeError exception is thrown.

I note that the documentation does not further explicate on the concept of a "slot" - I assume they're using it as a synonym for "array element" (I'm aware that JavaScript arrays can be sparse-arrays that have a different internal representation, but that shouldn't affect how consumers see them - given that JavaScript arrays and objects are abstractions over a variety of data structures anyway).

The documentation on MSDN for the same does not provide any mention of edge-case arguments for the Array function or Array-literal [] syntax.

So my questions are:

  • What does undefined × 2 mean?
  • Why does Array(2) evaluate to [undefined × 2] instead of [undefined, undefined]?
  • Why does [,] have a length: 1?

To attempt to answer my own questions I consulted the ECMA-262 7.0 specification on Array Literals. There's an interesting note on elided array literal elements (emphasis mine):

Array elements may be elided at the beginning, middle or end of the element list. Whenever a comma in the element list is not preceded by an AssignmentExpression (i.e., a comma at the beginning or after another comma), the missing array element contributes to the length of the Array and increases the index of subsequent elements. Elided array elements are not defined. If an element is elided at the end of an array, that element does not contribute to the length of the Array.

So the part in bold answers my third question, at least: if the last element is "missing" then it does not exist, and if a non-terminal element doesn't have a value then it's undefined. So these expressions are equivalent:

[a,]   --> [a]
[a,b,] --> [a,b]
[a,,b] --> [a,undefined,b]

Therefore

[,]    --> [undefined]
[,,]   --> [undefined,undefined]

So I tried this in Chrome:

> var a = [,,];
> a
> [undefined × 2]
>    length: 2

This is unexpected! I thought [,,] to be equivalent to [undefined,undefined] as per the explicit rules of element elision, yet Chrome considers [,,] to be equivalent to Array(2) instead (note there are no indexers defined, giving it the same result as case y).

So my forth question:

  • Why is [,,] equivalent to Array(2) but not [undefined,undefined]?
Dai
  • 141,631
  • 28
  • 261
  • 374
  • 1
    My gut feeling is that this is because of chrome internally representing `new Array(2)` as something sparse, `[undefined, undefined]` as something with a fixed size and `[,,]` as sparse as well. It's worth noting that `[1,,,2]` and `[1,undefined,undefined,2]` show similar behaviour. – david Jun 07 '17 at 05:25
  • I'm curious, and I can't test this myself now since I'm using a phone, but what would this look like?: `var one; var two; var a = [one, two]; console.log(a);` – Lennholm Jun 07 '17 at 05:36
  • @MikaelLennholm I have added your example in my answer. It prints proper values. Its similar to `[undefined, undefined]` as you are specifying values. – Rajesh Jun 07 '17 at 05:43
  • @david If that's so, it seems odd that Chrome would use an internal sparse-array structure for small arrays - surely the sparse-array overhead would only make it worthwhile for arrays larger than at least a few hundred elements. – Dai Jun 07 '17 at 05:47
  • See also [What is “`[undefined x 1]`” in JavaScript?](https://stackoverflow.com/q/10683773/1048572) – Bergi Jul 04 '17 at 17:51

1 Answers1

2

What does undefined × 2 mean?

Consoles display what some developer has decided is a useful message based on some input. It should not be regarded as normative in any sense, and in some cases is misleading. The statement:

var x = Array(2);

Creates a variable x and assigns a new array with length 2. There are no "empty slots" in any meaningful sense. It is equivalent to:

var x = [,,];

(noting that old IE had a bug where the above created an array with length 3 not 2). You can think of it as an object like:

var x = {length: 2};

The number of properties that can be added is in no way limited by setting the length. However setting the length of an existing array will delete any members with indexes equal to or higher than the new length.

Having said that, implementations are free to allocate space for "length" worth of indexes, which seems to be a performance boost in some cases, but it's not specified as being required by ECMA-262.

Why does Array(2) evaluate to [undefined × 2] instead of [undefined, undefined]?

See above, the console is just trying to be helpful. There are no undefined members at all, there is just an array with length 2. For the record, IE 10 shows:

[undefined, undefined]

which is also misleading for the above reasons. It should show [,,].

Why does [,] have a length: 1?

That is called an elision, which is part of an array initializer or array literal. It creates a "missing" member in the same way that:

var x = [];
x[0] = 0;
x[2] = 2;

does not have a member at index 1 and is equivalent to:

var x = [0,,2];

Noting again that old IE interpreted [,] as having length of 2. The member at index 1 is said to be "elided".

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Great explanation. Just confirming, will `new Array(10)` initialize 10 memory locations + 1 for length or will just initialize 1 with length's value and would allocate new memory on assignment? – Rajesh Jun 07 '17 at 06:05
  • It will create an array with length 10, that's it. Some implementations may allocate some memory based on *length*, but that's not required, should not be expected and is likely not consistent. – RobG Jun 07 '17 at 08:10