Why does the 1st map
method below treat element #2 as undefined
but the 2nd map
method does not?
const a = [undefined,,42]; // Note the two consecutive commas.
console.log(a.map(x=>x)); // -> [undefined, undefined, 42]
console.log(a.map(x=>x===undefined)); // -> [true, undefined, false]
I expand on this with the code below. There I have some numbered examples illustrating my points. In each case I create a 3-element array and from then on I am only interested in the 2nd element. I show that this 2nd element can be considered to be undefined
both by how it responds to .toString()
(i.e. how it appears in console.log(elmt2)
) as well as how it behaves in an equality statement (i.e. elmt2 === undefined
?). Then, however, I map all the elements in the array to a constant string irrespective of what the values are (i.e. array.map(_=>'foo')
), and look at the 2nd element of the mapped result. I would have expected all the outputs to be the constant string, but they aren't.
const check = (i, array) => {
const mapped = array.map(() => 'mapped ');
const equalsUndefined = (array[1] === undefined);
const mappable = (array.map(_=>1)[1] === 1);
console.log(`${i}: ${array[1]} ${equalsUndefined} ${mapped[1]} ${mappable}`);
};
const uExplicit = [4, undefined, 4][1];
const uEmptySlot = [4, , 4][1];
const uNewArray = (new Array(3) )[1];
console.log('#: origValue =undef? mappedValue mappable?');
check(1, [4, undefined , 4]);
check(2, [4, , 4]);
check(3, new Array(3) );
check(4, [4, uExplicit , 4]);
check(5, [4, uEmptySlot, 4]);
check(6, [4, uNewArray , 4]);
/*
results:
#: origValue =undef? mappedValue mappable?
1: undefined true mapped true
2: undefined true undefined false
3: undefined true undefined false
4: undefined true mapped true
5: undefined true mapped true
6: undefined true mapped true
*/
Line 1 shows that an explicit undefined
value can be successfully mapped in this manner.
However, lines 2 & 3 show that array elements that might be considered "empty slots" cannot be successfully mapped, even though they behave as undefined
according to the two tests described above. In line 2, the value is extracted from a literal empty space between two otherwise normal array elements. In line 3, the value is extracted from an array created using new Array(arrayLength)
.
Bizarrely, lines 3, 4 & 5 show that the values examined in lines 1, 2 & 3 respectively can be successfully mapped if they are simply assigned to a variable first, instead of being used directly.
To be clear, the MDN documentation for arrays states that new Array(arrayLength)
creates "...an array of arrayLength empty slots, not slots with actual undefined values". However, the output here shows that these values are treated as undefined
, at least with respect to how they behave with .toString()
and in equality statements.
So, are there two different "flavours" of undefined
in JavaScript, one that can be manipulated using map
and one that cannot? Is this behaviour expected/blessed (even if it's perhaps somewhat counter-intuitive)? Is there a safe way to map
through an array in such a way that all undefined
values can be treated identically?
UPDATE: THE SOURCE OF MY UNCERTAINTY
I have discovered why I was puzzled. It turns out that the Stack Overflow code snippet tool produces an ambiguous output when using console.log
in this case which lead me to think something was undefined
when in fact it was non-existent.
For the simple command console.log([undefined,,4])
, the consoles in Chrome, Firefox, Safari and Opera produce correct and unambiguous output:
Chrome:
Firefox:
Safari:
Opera:
However, the output from the code snippet tool on Stack Overflow (in any browser) is ambiguous and at least misleading if not technically incorrect (depending on how you define what console.log
should do). (While I eventually posted this question on Software Engineering, which does not have the code snippet tool, I originally worked through the problem on Stack Overflow, which does, leading to me seeing the confusing output shown below.) I have to split the input and output into two separate images just because they are on different parts of the screen...
Stack Overflow code snippet tool input:
Stack Overflow code snippet tool output:
Note that in this output the first and second elements are shown identically, i.e. as undefined
, in contrast with all the browser console outputs above which show the first element as undefined
but the element in the second position as non-existent.
Just for people's interest, note the following bug in Internet Explorer 11 when I try to show the same thing in its console:
Array [ undefined, <1 empty slot>, 42 ]
andArray [ true, <1 empty slot>, false ]
, respectively. This seems to support the accepted answer.