Another Javascript surprise: undefined vs undefined
-
You'd say that an array with undefined values is an array with undefined values, right? Not in Javascript. There is a difference between var a = new Array(3), and var b = [undefined, undefined, undefined]. Both are arrays with 3 undefined values, and a[i] === b[i]. They behave identical in a loop. So far so good. But now try a.map(fun) vs b.map(fun). The second one returns [fun(undefined), fun(undefined), fun(undefined)], as you expect, but the first one returns something like new Array(3): 3 undefined values, and fun never gets called. And filter is even less consistent: a.filter(fun) simply returns [], whatever function you provide.
Yes, it gets mentioned in the documentation (of map, but not of filter), but ... WHY? Who the fuck thought that would be a good idea?
-
It is silly, but I'm surprised this came up in general use. Undefined is meant for comparison purposes only and isn't anything you should be assigning to variables or properties; null is a better choice for a reference to nothing. If you ever need to serialize this object or another containing it to JSON, the spec doesn't cover undefined: http://www.json.org/
Though you probably knew this and I just came across as incredibly condescending and arrogant.
Out of interest, what's the use case for using the array constructor rather than an anonymous array? I used to use it back when I thought everything was C#/Java.
-
There was already a thread about this at some point.
var a = Array(3)
creates a sparse array, that has the declared length of 3, but no values. When you doa[1]
, javascript checks value at 1, finds nothing, and returns theundefined
"token".var b = [undefined, undefined, undefined]
, on the other hand, is an array that is "filled" with 3undefined
tokens. Even thougha[1] === b[1]
is true, these are two different arrays.Note that there are implications with memory usage and performance. A sparse array with length of a billion will take no memory at all. An array with a billion
undefined
s will take the memory for a billion pointers.As for whether this is a good idea.... Well, when you think about it, this is inevitable when you have a first class value used as placeholder for "key not found", instead of throwing an error. They could have prevented you from feeding
undefined
values into hashes and arrays, but I guess no one thought of that during the two week period the language was designed in, and now it's tool late.
-
But how can you have a sparse array with a length of three but nothing in it? (rather than an array with items at indexes and spaces between) That doesn't really make any sense. Guess if you have some code which will for(var i = 0... over it it's useful shorthand sometimes.
-
That doesn't really make any sense.
I thought that was the point of designing a programming language in a single week?
-
But how can you have a sparse array with a length of three but nothing in it? (rather than an array with items at indexes and spaces between) That doesn't really make any sense. Guess if you have some code which will for(var i = 0... over it it's useful shorthand sometimes.
I guess it is declared with the length 3, but no memory is actually allocated underneath it.
The real question is, will this array:
var a = Array(3); a[2] = undefined;
, take memory for 3 pointers or 1?
-
Javascript is so high-level that you shouldn't (ideally, mustn't) care about it. The only exception is optimization - but when optimizing, the only answer you should accept for your question is profiler's output.
-
Note that there are implications with memory usage and performance.
That still allows map and filter to iterate over all elements, without any extra cost. There is really no reason to make the implementation show through only when using a specific function.this is inevitable when you have a first class value used as placeholder for "key not found", instead of throwing an error
Not really. new Array(3) creates an array of three elements, so it has indices/keys 0, 1 and 2.Nope, it just doesn't make any sense.
-
var a = new Array(3),
yeah, that won't do what you want....
// new-array polyfill that does the "right" thing Array.prototype.newArray = function(length) { return Array.apply(null, Array(length)).map(function () { return null; }); }
Try using that instead. ;-)
-
It is silly, but I'm surprised this came up in general use. Undefined is meant for comparison purposes only and isn't anything you should be assigning to variables or properties; null is a better choice for a reference to nothing.
I agree, but there are a lot of people who will tell you:Null = Undefined
Null != Nothing
-
new Array(3) creates an array of three elements
No it doesn't. It creates an object, of type Array, with a length property (value: 3) and no other properties defined. Therefore,
a.banana
,a['banana']
, anda[0]
all returnundefined
, as they are not defined members. The only thing that's defined isa.length
, which is3
.Don't get this confused with C-style arrays, it's totally different.
-
[,,,]
is equivalent tonew Array(3)
. No, that is not a fencepost error.
-
PHP does something similar where the last trailing comma doesn't generate an extra item.
-
Go requires a comma after any complete value inside a compound literal that has a newline before the closing brace.
-
Go requires a comma after any complete value inside a compound literal that has a newline before the closing brace.
[insert [this bothers my ocd] macro here]
-
That's correct style in C#, too.
-
That's correct style in C#, too.
I'm not sure if I knew that or not. It just seems wrong, though.
-
Ok first of all, "seems wrong" is an emotional outburst that doesn't belong in software development.
It makes it much easier to copy lists of stuff around. It constantly bugs the crap out of me that SQL doesn't allow it, so if I paste my list of enums (or whatever) into a SQL query, then I have to cursor up and delete the last comma every goddamned time.
-
Ok first of all, "seems wrong" is an emotional outburst that doesn't belong in software development.
It doesn't comport with human language syntax, OK? Time to recalibrate your shoulder aliens again.
It makes it much easier to copy lists of stuff around.
That actually makes sense. But from the perspective of someone who's not used to it it seems weird/unusual.
-
@Hanzo said:
new Array(3) creates an array of three elements
No it doesn't. It creates an object, of type Array, with a length property (value: 3) and no other properties defined.
You're living in denial. If it walks like a duck and quacks like a duck, it should iterate like a duck.
-
If it walks like a duck and quacks like a duck, it should iterate like a duck.
Is the cycle of duck reproduction something we really needed in a programming language?
-
JavaScript is about as fucked up as a duck's reproductive process.
-
about as fucked up
Close, maybe...
https://www.youtube.com/watch?v=_1v_EcjeIkg
[Trigger warning: Corkscrew]
-
It's always fun to see someone experience JS sparse arrays for the first time.
They're actually more like hash maps. Array functionality is mainly just provided for convenience.
new Array(3) creates an array of three elements, so it has indices/keys 0, 1 and 2.
No, it doesn't:
Basically, any object with a
length
property can be used as a duck-typed array (including strings). If it's not an array, it won't have theArray.prototype
methods, although even then it's easy enough to bind one of them to it and it'll happily process it as if it was an array:The question of "is x an array or not" is actually complicated enough that there's a dedicated function for it now:
Array.isArray(x)
.
-
Also worth mentioning is that "array" keys/indices are actually strings, and anything that can be cast to a string (i.e., anything) can be used as an array index.
You can quite easily run into situations like this:
-
Also worth mentioning is that "array" keys/indices are actually strings, and anything that can be cast to a string (i.e., anything) can be used as an array index.
That is the worst part. And the empty string is a legal property name too. For comical value, do
a=[]; a[a] = a;
then ask what the value ofa[a+a]
is
-
The really fun part is if you've changed the object's
toString
function.
-
No repro:
-
That is the repro. You recursively assigned
a
toa[""]
, then you accesseda["" + ""]
and it returneda
. Note that it doesn't print the""
property ofa
when it displays it, sincea
is an array. You could confirm that it has one, though, if you executea.hasOwnProperty("")
.
-
That is the repro
Ah. Apparently my understanding of the language has been found... wanting.
-
Also, since
a
is now recursive, you can follow the rabbit hole forever...
-
-
a[a][a][a][a][a][a][a]
would work, as well.
-
Ah. Apparently my understanding of the language has been found... wanting.
The joke was that in Javascript you can sow so much confusion with just one variable. I propose you refuse to understand these parts.
The really
funevil part is if you've changed the object's toString function.I will have no part in this
a[a][a][a][a][a][a][a] would work, as well.
aaaaaaaaaaaaaaaaaaaaaaaaa
-
-
Also, since a is now recursive, you can follow the rabbit hole forever...
And people at work wonder why I like constructively finite data structures…
-
Who doesn't like
undefined
for a property name? I sure do. And I dare you to muster a prediction what this evaluates to2*undefined !== undefined+undefined
It is nasty on two levels, I got it wrong for the wrong reasons. Here's a hint to throw you off-track:
[spoiler] ```
a={};
a[2*undefined] = a;
a[undefined+undefined] === a
-
Let's try that:
2undefined = NaN, because the multiplication triggers type coercion on undefined, which becomes NaN and 2NaN yields NaN.
undefined + undefined = "undefinedundefined" because the + operator means concatenation unless both operands are numbers.
So I'd have to say that your expression reduces to NaN !== "undefinedundefined" which is true.
-
Let me guess...
2 * undefined
andundefined + undefined
are bothNaN
NaN !== NaN
(Actually,NaN != NaN
, too)But... the array index is a string, and
"NaN" === "NaN"
.I actually thought about using that (
NaN
) for my "x
andy
are not equal, buta[x]
anda[y]
reference the same array element" example, but I decided that two different objects would be a better example.
-
undefined + undefined = "undefinedundefined" because the + operator means concatenation unless both operands are numbers.
Also...
-
So I'd have to say that your expression reduces to NaN !== "undefinedundefined" which is true.
You fared way better than I did. You got the result right
NaN !== NaN (Actually, NaN != NaN, too)
Right! Somehow I expected
NaN !== NaN
to be false becauseNaN === NaN
is false (like the SQL NULL semantics.)
-
You scared me.
I thought my Firefox actually crashed for a moment.
-
PHP does something similar where the last trailing comma doesn't generate an extra item
I was almost reconsidering my hate for php because of its low hardware requirements (discourse made me think about that in another server)
You just made me hate it again.
:edit: I misread your post, thought php did what js did in op
-
But... but...
Improved performance: PHP 7 is up to twice as fast as PHP 5.6
Imagine the speed benefits over Discourse!
-
I grabbed my pitchfork too early, I read that as php doing what javascript did in op.
You triggered me man
-
But how can you have a sparse array with a length of three but nothing in it?
That is literally the definition of a sparse array. That's what the "sparse" part means: not all slots contain a value. And "0 slots contain a value" certainly qualifies as "not all slots contain a value" for any array size > 0.
-
Javascript is so high-level that you shouldn't (ideally, mustn't) care about it.
How about "are not allowed to, by design":In [plain] English: the philosophy of JavaScript (to the extent that it has any philosophy) is that **you should not be able to observe what is going on in system memory, full stop**. This is so unbelievably out of touch with how real people write mobile applications, I can’t even find the words to express it to you. .. You need *serious, formal memory management guarantees* on mobile. And JavaScript, **by design,** refuses to provide them.
-
That is the worst part. And the empty string is a legal property name too. For comical value, do a=[]; a[a] = a; then ask what the value of a[a+a] is
Wat...
-
How about "are not allowed to, by design"
This article makes it look like it was a bad design decision.
-
This article makes it look like it was a bad design decision.
...are you saying it wasn't?