The Wat talk has been floating around for a long time:
https://www.destroyallsoftware.com/talks/wat
There are probably lots of articles explaining exactly what's going on in this video, but I'll add this to the mix, since JavaScript typecasting is actually a lot simpler than most people think.
To understand typecasting in JavaScript, you actually only need to know three rules:
-
If you try to do something that doesn't make sense because the types are wrong (like use a math function on a string), JavaScript will do type conversions until it makes sense. This means first converting non-primitives (objects, including arrays) to primitives (strings, by default), and if that's not enough, converting primitives to numbers.
(Objects will by default convert to strings, the only exception is if you override
valueOf
:{valueOf: () => 5}
will convert to a number rather than a string.) -
The
+
operator is special: if one side is a string, the other side will be converted to a string. Otherwise, conversions work like normal (so boolean + boolean will convert both sides to a number.) -
""
when cast to number is0
, but other non-number strings when cast to number becomeNaN
. SoNumber("1e1")
is10
andNumber("1f1")
isNaN
.
Once you know those three things, JavaScript typecasting will never trip you up, which includes most pitfalls involving ==
.
Other ==
pitfalls: JavaScript follows IEEE 754, or whatever standard it was that says NaN != NaN
. Also, null == undefined
and undefined == null
, but neither of them equal anything else. Otherwise, just do the conversions I listed above and you'll get the correct answer.
Other JavaScript pitfalls: You shouldn't start a line with {}
, since it'll be interpreted as an empty block, not an empty object literal. This only matters in a REPL, since you'd pretty much never start a line with {}
in any other context. Use ({})
if you need to start a line with an empty object literal in a REPL.
Converting an array
to string will get you array.join(',')
(i.e. the string representation of the array's elements, separated by ,
). Converting an object to string when it doesn't have a different toString
function defined (like array
does) will get you the string '[object Object]'
.
In reality, you should never need to know any of this for writing your own code, because you shouldn't use any implicit type conversions at all (besides the idiomatic typecasts mentioned below).
The exceptions (idiomatic typecasts) are '' + x
as a shorter way to write String(x)
, !!x
as a shorter way to write Boolean(x)
, and +x
as a shorter way to write Number(x)
. Again, DO NOT use implicit typecasts for anything else; most code styles and linters ban them. The best way to check equality is ===
(just never use ==
). ===
will be false
if the types don't match, and work like ==
otherwise (NaN === NaN
is still false
, you'll need to use isNaN
, but remember isNaN
will implicitly convert to number; you can use ES5's Number.isNaN
or you can just use typeof x === 'number' && isNaN(x)
if you're not sure if x
is a number).
On the other hand, this is pretty much everything you need to answer those "do you REALLY know JavaScript?" quizzes with all those trick questions.
Wat doesn't use it, but other trick questions will use ,
. In JavaScript, like in C, ,
outside of variable declarations is basically a higher-precedence version of ;
that only works for expressions. (so a(),b()
is basically the same as a();b();
except it's an expression that evaluates to the value of b()
, like for instance 1 + (a(),b())
means you run a()
and then get 1 + b()
). You should never intentionally use it.
So now we know enough to tell us what's going on here:
[] + []
-> String([]) + String([])
-> "" + ""
-> ""
[]
gets cast into a string ""
, so you get "" + ""
which concatenates to ""
.
[] + {}
-> String([]) + String({})
-> "" + "[object Object]"
-> "[object Object]"
[]
and {}
get cast into strings and concatenated
{} + []
-> +[]
-> Number([])
-> Number(String([]))
-> Number("")
-> 0
{}
is interpreted as an empty code block. +[]
is a cast to number. Casting an object to number generally involves casting it to string first, since that's the only primitive most support being converted to.
{} + {}
-> +{}
-> Number({})
-> Number(String({}))
-> Number("[object Object]")
-> NaN
Same thing going on, but with {}
instead of []
.
"wat" + 1
-> "wat" + String(1)
-> "wat" + "1"
-> "wat1"
"wat" - 1
-> Number("wat") - 1
-> NaN - 1
-> NaN
Pretty straightforward.
And now you have it! You've learned pretty much nothing you need to know to do actual JavaScript coding, but plenty you need to not being confused by people intentionally trying to confuse you! I hope this at least helps when you encounter a weird error some day because you forgot to use a linter!