It is very intresting example. And pretty good answer by Dan Tao. But I think that I can give a little additional explanation.
In the first case
new Array(5)
creates an empty object, then it passes throw function and gived length of 5. Because of missing any other arguments this object will get no allocated entries.
// Array(5) [ <5 empty slots> ]
And when you trying to "map" those entries nothing actualy happened because of missing real entries.
However, if you try after this step "array[0]" for example, it returns "undefined"...
In the next case you are using "Call" method of Array() function after first "new Array(5)" (but actualy it has no differense "Call" or "Construct" method of call is used with Array function).
Array.apply(null, new Array(5))
So "new Array(5)" already gived as result Array(5) [ <5 empty slots> ] and "Function.prototype.apply()" decomposes this array to the five parameters that Array() function gets in. And in the current step we get:
// Array(5) [ undefined, undefined, undefined, undefined, undefined ]
These are five real entries. And we can do "map()" with them. But there is a litle mistake in your result, because we currently get after
Array.apply(null, new Array(5)).map(function() { return new Array(5);
});
a little bit different result
/*
[…]
0: Array(5) [ <5 empty slots> ]
1: Array(5) [ <5 empty slots> ]
2: Array(5) [ <5 empty slots> ]
3: Array(5) [ <5 empty slots> ]
4: Array(5) [ <5 empty slots> ]
*/
and to get more precise, to get "five on five, undefined" result we need to little upgrade your code
Array.apply(null, new Array(5)).map(function(){ return Array.apply(null,new Array(5)); });
this will return "five on five, undefined" array.
/*
[…]
0: Array(5) [ undefined, undefined, undefined, … ]
1: Array(5) [ undefined, undefined, undefined, … ]
2: Array(5) [ undefined, undefined, undefined, … ]
3: Array(5) [ undefined, undefined, undefined, … ]
4: Array(5) [ undefined, undefined, undefined, … ]
*/
But what I'm talking about is that not only "Function.prototype.apply()" has current behavior to decompose array without real entries. I'll give you an example:
Array(...new Array(5)).map(() => Array(...new Array(5)));
this will actualy gived to us exactly the same result - five on five undefined.
If we took a closer look:
- In the first action
new Array(5)
Array() function returns an empty array but with length property value of '5' because it runs in "Construct" mode, and has one parameter (5).
- The second and third action
Array.apply() | Array(...)
first of all spreads Array without elements to 5 parameters and then pass them to an Array().
It's because of "apply()" or "..." behavior of decomposing arrays. When it gets length of an array it auto transform "empty slots" into undefined values.
Referense from Ecma-262/6.0
(http://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply)
19.2.3.1 Function.prototype.apply
1. If IsCallable(func) is false, throw a TypeError exception.
2. If argArray is null or undefined, then Return Call(func, thisArg).
3. Let argList be CreateListFromArrayLike(argArray).
7.3.17 CreateListFromArrayLike (obj [, elementTypes] )
1. ReturnIfAbrupt(obj).
2. If elementTypes was not passed, let elementTypes be (Undefined, Null, Boolean, String, Symbol, Number, Object).
3. If Type(obj) is not Object, throw a TypeError exception.
4. Let len be ToLength(Get(obj, "length")).
5. ReturnIfAbrupt(len).
6. Let list be an empty List.
7. Let index be 0.
8. Repeat while index < len
a. Let indexName be ToString(index).
b. Let next be Get(obj, indexName).
c. ReturnIfAbrupt(next).
d. If Type(next) is not an element of elementTypes, throw a TypeError exception.
e. Append next as the last element of list.
f. Set index to index + 1.
9. Return list.
- Here in '8a' clause we get "0", "1"... indexNames to pass them in '8b' as argument names of arraylike object (in our case just array, without any 'like') - array["0"], array["1"]...
- Each of requested element values returns with "undefined" and then in 8'e' they consecutively appends to the arguments list.
- "CreateListFromArrayLike" returns with five arguments of undefined in list representation in '9'clause which is passed to Function.prototype.apply() and will be applied to an Array() (it will be looking like
new Array(undefined,undefined,undefined,undefined,undefined)
).
Spread operator uses iteration protokol instead, but in practice they act simulary on this case.