54

I know JavaScript has lots of insane results with comparisons between types, though I don't fully understand why. Came across this one today.

Why does

"" == [null]

evaluate to true in JavaScript?

Some more Javascript equality amusement, thanks to @Qantas:

Community
  • 1
  • 1
Scott Stafford
  • 43,764
  • 28
  • 129
  • 177
  • 10
    An array with a single null element get's casted to a string, meaning it calls .join(',') on it, since there's only one element it becomes `(string)null`, which is an empty string. – scragar Aug 27 '14 at 15:41
  • 9
    Use strict equality `===`, if you want to compare two different types, convert them to the same type – Paul S. Aug 27 '14 at 15:46
  • 2
    Relevant: http://dorey.github.io/JavaScript-Equality-Table/ – Mike G Aug 27 '14 at 17:11
  • 4
    Related: [Why does 2 == \[2\] in JavaScript?](http://stackoverflow.com/questions/1724255/why-does-2-2-in-javascript), [Why is 0 == "" true in JavaScript](http://stackoverflow.com/questions/7605011/why-is-0-true-in-javascript), [Why if(\[\]) is validated while \[\] == false in javascript?](http://stackoverflow.com/questions/13799497/why-if-is-validated-while-false-in-javascript) and [Does it matter which equals operator (== vs ===) I use in JavaScript comparisons](http://stackoverflow.com/questions/359494/does-it-matter-which-equals-operator-vs-i-use-in-javascript-comparisons)? – Qantas 94 Heavy Aug 28 '14 at 06:29

6 Answers6

76

The "Abstract Equality Comparison Algorithm" has many parts, but the important one here is this:

If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).

(There's a mirror-image of that too.) So, because "" is a string and [null] is an object, we've got to first convert [null] to a string by calling ToPrimitive([null]). That's an internal operation described as follows, when it's asked to convert an Object instance to a primitive value:

Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.

Now, the [[DefaultValue]] internal operation will call .toString() on the object and return that value. Try [null].toString() in your browser console:

> [null].toString()
""

And there you have it.

Edit: And why is [null].toString() an empty string? Because the .toString() operation on Array instances always just calls .join(), and that always yields an empty string for null and undefined values. Thus an array of one null ends up as just a single empty string.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 1
    I think you're short a step; why doesn't `[null].toString()` return `"null"`? – Izkata Aug 27 '14 at 18:25
  • 8
    @Izkata `Array.prototype.toString()` returns the result of calling `this.join()` on the array, and `.join()` renders `null` and `undefined` entries as empty strings. Thus an array with one `null` entry renders as the empty string. – Pointy Aug 27 '14 at 18:31
  • 1
    Same as casting integer to string or boolean `"" == 0` and `"" == false` – sulest Aug 27 '14 at 19:24
  • 3
    @sulest No its not. In those cases, the string is being converted to the type of the other argument. In OP's question, the other argument is being converted to a string. – Aaron Dufour Aug 27 '14 at 19:46
  • 1
    @Pointy What I meant was, I think that comment would be good to add to the answer – Izkata Aug 27 '14 at 20:58
  • @Izkata yes I added a coda :) – Pointy Aug 28 '14 at 07:02
  • `"" == [null, null]` is false and `"" == [null]` is true, confusing :) – Ahmad Aug 28 '14 at 12:37
  • 1
    the simple fact is when you compare two values from different types using `==` not `===` it always converts variables to same type. when the case is `"" == 0` then the `""` will be converted to `Number("")` so the result will be `0 == 0`. When the case is `"" == false` then `Boolean("")` will give us `false == false` and so on. Why do we discuss this anyway :) – sulest Aug 29 '14 at 12:08
  • 1
    @Ahmad `[null, null].toString()` is `','`, try: `[null, null] == ','`, `[null, null, null] == ',,'` – sulest Aug 29 '14 at 12:12
  • 1
    @sulest: The reason we discuss it is because so many people fall afoul of it. For me (and many others, I'm sure) other languages use `==` to mean javascript's `===`, so it needs to be learned the hard way... ;) – Scott Stafford Sep 03 '14 at 13:39
17

It's according to the arcane type-conversion rules of Javascript. Rule #8:

If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).

So the comparison is between x = "" and y = [null] converted to a string using ToPrimitive. Converting an array with one null element results in an empty string (because Array.toString() returns a comma-separated list of values), hence they evaluate to equal.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
12

Why does "" == [null] evaluate to true?

Because you're comparing an array with a string, using the non-strict equality operator == - so it will try to cast values to the same type before comparing them.

What happens in detail is:

  1. you compare a string to an object, so the object is cast to a string:
  2. When an array is cast to a primitive value, it's .toString() method is invoked (as explained in detail by the other answers), which is equivalent to calling .join():
  3. which in case of a one-element array that only contains an undefined or null value returns the empty string
  4. which finally is equivalent to the empty string

This third step is the unexpected one ([null]+"" != null+""), if it actually did cast that to a string the result would have been "null" and your equality be false.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
2

Let's look at the spec and follow through each step

Going via the Abstract Equality Comparison Algorithm (§11.9.3):

  1. typeof ""; // string and typeof [null]; // object so not applicable
  2. neither is null or undefined so not applicable
  3. same as 2
  4. neither is a number, not applicable
  5. same as 4
  6. neither is a bool, not applicable
  7. again not applicable
  8. finally, something applicable, now we need to know ToPrimitive([null])

§9.1 ToPrimitive for Objects says we need to work out [[DefaultValue]] (§8.12.8), points 1 and 2 of which say if you can do .toString and it gives a string, return that, so

[null].toString(); // ""

So we are now performing the comparison "" == "" which is true by the Abstract Equality Comparison Algorithm's point 1. d.

If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Paul S.
  • 64,864
  • 9
  • 122
  • 138
1

JavaScript is weakly typed; you can use the following to get a false result:

"" === [null]
Matt
  • 74,352
  • 26
  • 153
  • 180
-1

The value null is a JavaScript literal representing null or an "empty" value, i.e. no object value is present. It is one of JavaScript's primitive values.

The value null is a literal (not a property of the global object like undefined can be). In APIs, null is often retrieved in place where an object can be expected but no object is relevant. When checking for null or undefined beware of the differences between equality (==) and identity (===) operators (type-conversion is performed with the former).

typeof null        // object (bug in ECMAScript, should be null)
typeof undefined   // undefined
null === undefined // false
null  == undefined // true