2

What does the following actually create?

I ask because it cannot be enumerated with Array#foreach and it is represented (misleadingly?) in Chrome console as [undefined x 10].

const arr = new Array(10);
11
  • console.log(arr);. It will create an array with size 10 and undefined values. jsfiddle.net/o9f25Ljz Commented Jul 4, 2017 at 14:11
  • No, i don't think it will. Please try it. Commented Jul 4, 2017 at 14:11
  • 2
    The runtime distinguishes array elements that have been explicitly assigned values from those that haven't. Commented Jul 4, 2017 at 14:12
  • 2
    You can't enumerate, because array doesn't contain any element. You are confused because it looks like there are 10 undefined values, but it's not the case, there are nothing, "holes". Commented Jul 4, 2017 at 14:15
  • 2
    const variables cannot be re-assigned. Object contents can, meanwhile, be changed. Commented Jul 4, 2017 at 14:24

3 Answers 3

3

The Array object has three different possible constructor actions:

  1. new Array()
  2. new Array(<multiple arguments)
  3. new Array(<one argument>) (placed as last because of its nature - see further)

The behavior of the constructor depends of the arguments provided to that constructor. All those three actions uses ArrayCreate during the constructor action. From the link:

The abstract operation ArrayCreate with argument length (a positive integer) and optional argument proto is used to specify the creation of new Array exotic objects.

This function accepts two arguments: len (length) and proto which is used to create an array object. (I won't go deeper here because it would be more complex). The len argument is more important in this situation. When we are looking to the constructor actions individually, you have

new Array()

Here, an object is constructed by ArrayCreate(0, proto), thus an empty array with .length = 0.

new Array(<multiple arguments>)

Here, an object is constructed by ArrayCreate(#of args, proto). If you provide 3 arguments, then you get an array with .length = 3. This array is then populated with the provided arguments. Do you give 5 arguments? You get .length = 5. So the .length property depends of the number of the arguments.

new Array(<one argument>)

This one is quite different. It has two cases. At init, an array object is created with ArrayCreate(0, proto) like if you're using new Array(). Still we have an argument here to use.

If the argument is a non-numeric value, then the effect is same as point 2. This argument is being added to the constructed object (like a .push() operation. So new Array('hello') or new Array('10') gives you an array object with .length = 1

Now, the thing is very different when you give a numeric argument new Array(10), which gives in Chrome [undefined x 10]. Why? Because when the argument is numeric (and valid), this function is being called: Set(O,P,V,Throw). This "magic" function sets the property P of object O with the value V. (The Throw is a flag to indicate " throw error or not? " question).

If the argument is a length, set(<constructed array>, 'length', <argument>, true) is being executed. So the .length property returns the argument value while there is nothing being preserved in the memory. End result: you have an object with a fake .length property... A side-effect of this is that using .push adds an element to the 10th index (if .length was 10). Now you have an array with 11 elements: 10 "empty" slots and an element that you've just pushed.

So what does the browsers do with an array with a "fake" .length property? They cannot iterate it correctly. So they provide a failsafe way: display "has 10 elements but are empty" like chrome does with [undefined x 10] or firefox: Array [ <10 empty slots>]

If you ask me why this is being standardized in the specs, then I would respond with "I don't know". It is one of the weirdest decision (among other ones that I have encountered) that is being standardized in ECMAScript.

Sign up to request clarification or add additional context in comments.

Comments

1

The following two snippets are equivalent:

const arr = new Array(10)

and

const arr = [,,,,,,,,,,]

as can be demonstrated here:

const arr1 = new Array(10)
const arr2 = [,,,,,,,,,,]

let empty1 = true

for (let index = 0; empty1 && index < arr1.length; index++) {
  if (arr1.hasOwnProperty(index)) {
    empty1 = false
  }
}

let empty2 = true

for (let index = 0; empty1 && index < arr2.length; index++) {
  if (arr2.hasOwnProperty(index)) {
    empty2 = false
  }
}

console.log(empty1)
console.log(empty2)

This means that for all indices from [0, length - 1], they don't actually exist on the array because arr.hasOwnProperty(index) is false. This is distinguished from this example:

const arr = new Array(10).fill()

demonstrated here:

const arr = new Array(10).fill()

console.log(arr.every((_, index) => arr.hasOwnProperty(index)))

So if you want to initialize each index in a constructed array with undefined, just call .fill() without any arguments.

Comments

0

The Array constructor when supplied with a single integer value creates an Array instance with the length own-property set to that value.

Host environments like browsers can choose to render this as they choose. V8 chooses [undefined x 10] which is misleading because it implies there are ten instances of the predefined undefined value in an ordered sequence.

This is not the case.

Array#foreach, Array#map use the enumerable integer indices AND the length own-property value to enumerate the values of an array.

Because this specific Array constructor syntax does not initialize any integer properties on the Array object, they fail to enumerate Arrays initialized in this manner.

I can only presume that this syntax is soley present to enable code patterns that rely on the length own-property of the Array instance WITHOUT needing any keys or values to be initialized.

4 Comments

from developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… : forEach() executes the provided callback once for each element present in the array in ascending order. It is not invoked for index properties that have been deleted or are uninitialized (i.e. on sparse arrays). - if you want to add to your post
You state that the constructor doesn't initializes any integer properties, but then why arr[0] outputs something? -> jsfiddle.net/o9f25Ljz/1
The value of a not-present property is undefined.
if I do console.log(newArr[0]); does output nothing(without initialization of newArr) - not undefined

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.