Consider:
var a = Array(3);
var b = [undefined,undefined,undefined];
What's the reason that a.map
and b.map
produce different results?
a.map(function(){ return 0; }); //produces -> [undefined,undefined,undefined]
b.map(function(){ return 0; }); //produces -> [0,0,0]
The array constructor creates an array with the given length. It does not create the keys. Array.prototype.map
's callback function is only executed for the elements in the list.
That is, all values which are associated with a key (integer) 0 ? i < length.
Array(3)
has zero keys, so .map
's callback is never triggered.[void 0, void 0, void 0]
has three keys, for which the callback function is executed.
Array(3).hasOwnProperty(0); // false
[void 0, void 0, void 0].hasOwnProperty(0); // true
The specification and its polyfill are mentioned at MDN. At line 47, if (k in O) {
shows that non-existant keys are not treated by the callback function.
map
only iterates existing properties, not empty indices.
Therefore, if you want it to work, you must first fill the array.
There are multiple ways to do that, for example:
.fill()
, introduced in ES6
console.log(new Array(3).fill().map(function(){ return 0; }));
var arr = [].concat.apply([], new Array(3));
console.log(arr.map(function(){ return 0; }));
An old for
loop.
var arr = new Array(3);
for(var i=0; i<arr.length; ++i) arr[i] = 1; /* whatever */
console.log(arr.map(function(){ return 0; }));
Use some idea from Most efficient way to create a zero filled JavaScript array?
Etcetera.
a
is an empty array that doesn't have elements, so map function produces empty array without elements (per specification, map produces results only if [[HasProperty]] is true.) b
is an array of three elements, so map produces an array of three elements.
Array(len)
creates an array and sets its length accordingly but only its length is "enumerable", not the values contained. So, you cannot map the array Array(100).map(/* nope */)
— it's not a "real array" yet and it is actually empty despite having the correct length.
callback is invoked only for indexes of the array which have assigned values including undefined.,
The array
does not contain any values; not even undefined
It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
To populate array you need to iterate it someway… E.g.: [...Array(100)]
or Array.from(Array(100))
I imagine the purpose of this initialization is to optimize memory allocation… there isn't really anything in the array. MDN says "empty arrayLength
objects" which might be misleading as trying to access any of the "empty items" just returns undefined… But we know they aren't really undefined
since map
fails, therefore we can confirm it is a truly empty array.
This example does not seek to mirror specification but instead to illustrate why an array
returned from Array
cannot yet be iterated
function * (length) {
const arr = [];
Object.defineProperty(arr, 'length', { value: length });
// Equivalent, but invokes assignment trap and mutates array:
// arr.length = length;
Object.defineProperty(arr, Symbol.iterator, {
value() {
let i = 0;
return {
next() {
return {
value: undefined, // (Not omitted for illustration)
done: i++ == length
};
}
}
}
})
return arr;
}
It is worth pointing out that despite providing a undefined
value in the generator value property, it does not recognize it as a value and so the array is empty.
Array(len)
Specificationhttps://www.ecma-international.org/ecma-262/6.0/#sec-array-len
Array (len) This description applies if and only if the Array constructor is called with exactly one argument.
1) Let
numberOfArgs
be the number of arguments passed to this function call.2) Assert:
numberOfArgs
= 1.3) If NewTarget is
undefined
, letnewTarget
be the active function object, else letnewTarget
beNewTarget
.4) Let
proto
beGetPrototypeFromConstructor(newTarget, "%ArrayPrototype%")
.5)
ReturnIfAbrupt(proto)
.6) Let array be
ArrayCreate(0, proto)
.7) If Type(
len
) is not Number, thena) Let
defineStatus
beCreateDataProperty(array, "0", len)
.b) Assert:
defineStatus
is true.c) Let
intLen
be 1.8) Else, a) Let
intLen
be ToUint32(len
). b) IfintLen
?len
, throw a RangeError exception.9) Let
setStatus
be Set(array, "length", intLen, true).10) Assert:
setStatus
is not an abrupt completion.11) Return array.
From MDN:
callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
For the array a
, you've instantiated an array of length 3 but have not assigned any values. The map function finds no elements with assigned values, so it does not produce a new array.
For the array b
, you've instantiated an array of 3 elements, each with the value undefined
. The map function finds 3 elements with assigned values, and returns '0' as the new value for each of them in a new array.
©2020 All rights reserved.