# The WATs of JavaScript

At CodeMash 2012, Gary Bernhardt gave a now infamous lightning talk
that has become known simply as The WAT Talk, in which he presents
several of the more surprising behaviors of Ruby and JavaScript. I've
passed the video around quite a few times, and I've pointed out some
other JavaScript behaviors that seem pretty outlandish at first
sight. But I'm feeling a little guilty about poking fun at JavaScript,
so I wanted to dive further into these WATs and talk about *why* they
happen.

I'm not here to defend JavaScript — it doesn't need my defense. It's not a perfect language, and it's not my favorite language, but it is the single most popular language on the web right now, and because more and more people are using it for the first time, I think it's worth the effort to go over some of these unexpected behaviors to help newcomers avoid common pitfalls.

But first, the WATs.

## The WATs

> [] == [] false > [] == ![] true > [] + [] '' > [] - [] 0 > [ null, undefined, [] ] == ',,' true > [] + {} '[object Object]' > {} + [] 0 > Math.min() < Math.max() false > 10.8 / 100 0.10800000000000001

**WAT?!**

## Background: Type Coercion

I'm putting the cart before the horse a bit here, but I'm going to go
ahead and spoil bits of the rest of this post by telling you up-front
that many of these WATs involve *type coercion*. JavaScript is a
dynamically typed language, but it is also a weakly typed language,
meaning that variables may be automatically changed from one type to
another in fairly wide contexts. Coercion is distinct from casting, in
which a variable is changed to another type, but only if you're
changing the interpretation of its contained data or if the new type
is higher up in the same type hierarchy. For example, in Java when you
cast a long to an int, you're asking that the top 32 bits be ignored
and the remaining bits be treated as a 32-bit signed integer; when you
cast a String to an Object, you're asking the compiler to treat calls
to the reference as if they were calls to an instance of Object.

Type coercion, on the other hand, is happy to turn an int into a String for you. In a language like Java, it happens only in a very few places, and is usually pretty obvious and easy to predict. If you're used to these rules, though, JavaScript's coercion can be pretty surprising. Not only will it turn a Numeric type into a String for you, but it will go the other direction too.

And now, without further ado, on to the list of WATs.

## WAT Number One: Empty Array Not Equal To Empty Array

```
[] == [] => false
```

There's actually a lot going on here. At first glance, it looks
absurd: *an empty array does not equal itself?!* But that's not really
the question we asked.

So, what question *did* we ask? Like a Facebook relationship status,
it's complicated. JavaScript's double-equals equality operator has a
fairly extensive set of rules that make it more difficult to use than
the double-equals operator in other C-like languages.

What we're really asking here is, "Is one instance of an empty array
equal to another instance of an empty array?" In JavaScript, the
answer is no, because when you're comparing two instances *of the same
type*, double-equals will return true if and only if they are the same
instance. We've created two instances here, one on the left side of
the operator, and one on the right side of the operator. They are not
the same instance, so [] == [] evaluates to false.

## WAT Number Two: Empty Array Equals Bang Empty Array

```
[] == ![] => true
```

OK, now what is *this* nonsense?

Although I agree it's pretty confusing, it's not nonsense *if* you
know the rules and *if* you apply them in the right order. Here, the
bang operator binds more tightly than the double equals, so the first
thing we have to do is evaluate ![]. It so happens that in JavaScript,
the bang operator coerces whatever it's applied to into a boolean
value. An empty array, in JavaScript, is considered “truthy”, so when
it's coerced into a boolean, it becomes true, not false, and the bang
operator converts the true into a false. Now the question looks more
like this:

[] == false => true

OK, but that *still* looks bizarre, right? We just said that an empty
array was truthy, not falsy, and true certainly does not equal false!

But, like I said, double-equals is complicated. The rules say that
when you're comparing an Object on the left and a boolean on the
right, you convert both sides to *numbers* (the same as applying the
Number() function), and re-run the comparison.

In JavaScript, `Number([]) => 0`

, and `Number(false) => 0`

, so now we
have a new question:

```
0 == 0 => true
```

That's more like it. Yes, zero is equal to zero.

## WAT Number Three: Empty Array plus Empty Array Yields Empty String

```
[] + [] => ''
```

This is actually fairly straight-forward once you understand that the addition operator in JavaScript only operates on two types: Strings and Numbers. We're asking JavaScript to add two Arrays, which it doesn't know how to do, so it coerces them both into Strings and then concatenates them together. Calling [].toString() yields an empty string, and concatenating two empty strings together yields an empty string.

## WAT Number Four: Empty Array minus Empty Array Yields Zero

[] - [] => 0

This case is *very* similar to the previous case, except we're using
the minus operator instead of plus. Again, JavaScript doesn't know how
to subtract arrays, and while Strings can be concatenated with the
plus operator, Strings don't understand the minus operator at all, so
JavaScript coerces both sides into Numbers for us, instead of
Strings. If you call Number([]) in JavaScript, you get 0, so what
we're really saying is:

0 - 0 => 0

That makes a lot more sense.

## WAT Number Five: An Array Of Values Is Equal To A Weird Looking String

[ null, undefined, [] ] == ',,' => true

Woah, hey now. What's this all about? It's all about coercion and that double-equals operator again.

Here, we are using double-equals to compare an Object and a
String. The rules say that when you're comparing an Object on the left
side and a String on the right side, you have to perform some
operations to the left hand side to figure out how to do the
comparison. The end result of this is that the array on the left is
coerced into a String (by calling its toString() function, if it has
one). And it just so happens that calling [null, undefined,
[]].toString() yields the String ",," /(Why? Because null, undefined,
and [] all turn into empty strings, and JavaScript joins those empty
strings with commas)/. So we're *really* asking if two strings are
equal:

',,' == ',,' => true

Yes, they are, so it evaluates to true.

## WAT Number Six: Array Plus Object Yields The String “[object Object]”

```
[] + {} => '[object Object]'
```

This is still more type coercion. We've already discussed how the addition operator only operates on Numbers and Strings, and what's happening here is JavaScript converting both sides of the operator into Strings. An empty array, as we've already seen, gets converted into an empty string. But what about {}?

In JavaScript, `{}`

is (usually!) an Object literal. It's really
saying, “Make an instance of Object for me, please.” So now we're
concatenating an empty string, and the result of coercing an Object
instance into a String.

Well, the default `toString()`

for Object returns the string "[object
Object]". The code now becomes:

'' + '[object Object]' => '[object Object]'

## WAT Number Seven: Object Plus Array Yields Zero

{} + [] => 0

BUH!

Wait wait wait. We *just* got finished explaining how JavaScript
coerces both sides into a String, right? So shouldn't this be exactly
the same as the previous example?

Well, not quite.

We've actually been bitten by a bizarre edge case here. JavaScript has
misunderstood our intentions completely. As it scanned the line, it
interpreted the initial {} not as an object literal, but rather as an
*empty code block*. Yes, really. In essence, it has just decided to
ignore it completely, and treat the line as if it were written this
way:

+ [] => 0

That in itself would seem nonsensical, except that JavaScript also allows unary addition operator. Unary addition + x is equivalent to calling Number(x), so what you end up with is an empty Array instance being converted to a number, which yields 0.

## WAT Number Eight: Math.min() is greater than Math.max()

```
Math.min() < Math.max() => false
```

Before we explore this, let's look at how these functions are defined, and what they return.

Math.min() and Math.max() are being used *deceptively* in this
WAT. Most languages define global min and max values, and JavaScript
does too — but not with Math.min() and Math.max(). Instead, JavaScript
gives us Number.MIN_VALUE and Number.MAX_VALUE, and these behave
exactly as you would expect:

> Number.MIN_VALUE < Number.MAX_VALUE true > Number.MIN_VALUE > Number.MAX_VALUE false

Math.min() and Math.max(), on the other hand, are functions used to find the min and max values of a sequence of numbers. If you use them correctly, they make sense:

> Math.min(27, 9, 13) 9 > Math.max(27, 9, 13) 27

And if you call them without arguments?

> Math.min() Infinity > Math.max() -Infinity

Why? It's how they're implemented. Math.min() initializes its return value to Infinity and then looks for the smallest number in the sequence. There isn't one, so it returns Infinity. Similarly, Math.max() initializes its return value to -Infinity, and looks for the largest value. There isn't one, so it returns -Infinity.

The final form of this makes a lot more sense:

Infinity < -Infinity => false

## WAT Number Nine: Floating Point Madness

10.8 / 100 => 0.10800000000000001

This is probably the sanest WTF on the page. If you've ever worked with floating point numbers in any language, this kind of thing is probably already familiar to you. It's just a run of the mill floating point accuracy issue.

But 100 is an integer, isn't it? Well, no. Not in
JavaScript. JavaScript numbers come in only one flavor: IEEE-754
floating point. There *are no integers in JavaScript*. This may in
fact be the single most important thing to know about JavaScript,
especially if you're doing any kind of math.

This problem would present itself in C, too. Example:

#include "stdio.h" int main(void) { float f = 10.8f / 100.0f; printf("%.16f\r\n", f); }

If you run this code, what do you get? Not 0.108! On my computer, you get 0.1080000028014183, because of inherent limitations in floating point accuracy.

JavaScript isn't alone here, it's just more likely to bite you because
you *have* to use floating point, while you're free to use other
numeric types in other languages.

## Conclusion

I'll be frank: JavaScript is *weird*, but it's here to stay, and if
you find yourself working in it, it's good to immerse yourself in the
weirdness and try to get a feel for what's going on under the hood. I
hope this post has helped to clear up a handful of the stranger
seeming cases, especially if you're new to it and coming from the
background of another language.