42

I've been reading Douglas Crockford's JavaScript: The Good Parts, and I came across this weird example that doesn't make sense to me:

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == undefined  // false
false == null       // false
null == undefined   // true

The author also goes on to mention "to never use == and !=. Instead, always use === and !==". However, he doesn't explain why the above behavior is exhibited? So my question is, why are the above results as they are? Isn't transitivity considered in JavaScript?

2540625
  • 11,022
  • 8
  • 52
  • 58
Hristo
  • 45,559
  • 65
  • 163
  • 230
  • 2
    The best way to answer this question is to read the specification: http://www.ecma-international.org/publications/standards/Ecma-262.htm If you really want to know the nitty-gritty of why the statements above are the way they are, there's really no better way. Be prepared that it can be a bit of a slog in places, but if you're asking a question like this, it's well worth the effort. – T.J. Crowder Mar 27 '11 at 04:35
  • @T.J. Crowder Very good suggestion (+1), I edited that link into my answer, hope you don't mind. – alex Mar 27 '11 at 04:41
  • 4
    It should be pointed out that the second set of equalities involving `false`, `undefined`, and `null` does not violate transitivity - it's the same as `A != B, A != C, B == C`. – Nacht Oct 09 '12 at 05:50
  • @Nacht... yes, you're right! but the first one is weird :P – Hristo Oct 09 '12 at 05:57
  • See level 3: http://alf.nu/ReturnTrue – JohnLBevan Aug 12 '16 at 12:25
  • Another one is `false==''`, `false==' '`, but `''!=' '`. In words, both the empty string and the string with a single space in it are both false(y) but not equal. – vrugtehagel Jul 02 '17 at 20:23
  • I can't agree more on this - **JavaScript equality transitivity is weird**. – RBT Nov 24 '17 at 11:11

3 Answers3

35
'' == '0' // false

The left hand side is an empty string, and the right hand side is a string with one character. They are false because it is making a comparison between two un identical strings (thanks Niall).

0 == '' // true

Hence, why this one is true, because 0 is falsy and the empty string is falsy.

0 == '0' // true

This one is a bit trickier. The spec states that if the operands are a string and a number, then coerce the string to number. '0' becomes 0. Thanks smfoote.

false == undefined // false

The value undefined is special in JavaScript and is not equal to anything else except null. However, it is falsy.

false == null // false

Again, null is special. It is only equal to undefined. It is also falsy.

null == undefined // true

null and undefined are similar, but not the same. null means nothing, whilst undefined is the value for a variable not set or not existing. It would kind of make sense that their values would be considered equal.

If you want to be really confused, check this...

'\n\r\t' == 0

A string consisting only of whitespace is considered equal to 0.

Douglas Crockford makes a lot of recommendations, but you don't have to take them as gospel. :)

T.J. Crowder makes an excellent suggestion of studying the ECMAScript Language Specification to know the whole story behind these equality tests.

Further Reading?

The spec.

yolpo (on falsy values)

Community
  • 1
  • 1
alex
  • 479,566
  • 201
  • 878
  • 984
  • 3
    *"Douglas Crockford makes a lot of recommendations, but you don't have to take them as gospel."* Well put. Crockford is a smart person who's thought a lot on purpose about the language, and so it's well worth reading the points he raises and the arguments he makes. Then come to your own conclusions. – T.J. Crowder Mar 27 '11 at 04:44
  • @alex... in your third paragraph starting with "This one is a bit trickier", don't you mean `==` does check types? – Hristo Mar 27 '11 at 05:44
  • @Hristo `==` doesn't check types, just values. – alex Mar 27 '11 at 11:40
  • @alex... I disagree. https://developer.mozilla.org/en/JavaScript/Reference/Operators/Comparison_Operators. I think `==` does check types and values. `===` doesn't convert types, just checks strict equality. – Hristo Mar 27 '11 at 13:09
  • @Hristo Well it does check the types implementation wise (it will convert the types to make the comparison), but I thought it might be easier to mention *values*, i.e. `1 == '1'`. So in that example, `'1'` will be coerced to `Number`. – alex Mar 27 '11 at 13:51
  • @alex good answer overall, but your reasoning of why `'' == '0'` evaluates to false is incorrect. Both operands are the same type (String) so no type conversation takes place (therefore the falsyness of an empty string is not relevant here). The expression evaluates to false for exactly the same reason `'dog' == 'cat'` does, i.e. the operands are two different strings. – Niall Smart Jun 17 '11 at 21:52
  • @Niall You are totally correct and I'm not sure why I wrote that :P I'll make an edit. – alex Jun 17 '11 at 23:45
  • Your explanation for why `0 == ''` is true is also incorrect. ECMAScript always coerces strings to numbers when performing non-strict equality comparison of a string and a number. – smfoote Feb 27 '14 at 05:09
  • @smfoote Good catch, I should have consulted the spec more before I wrote this answer. – alex Feb 27 '14 at 10:01
8

The answer to this question has to do with how JavaScript handles coercion. In the case of ==, strings are coerced to be numbers. Therefore:

'' == '0' is equivalent to '' === '0' (both are strings, so no coercion is necessary).

0 == '' is equivalent to 0 === 0 because the string '' becomes the number 0 (math.abs('') === 0).

0 == '0' is equivalent to 0 === 0 for the same reason.

false == undefined is equivalent to 0 === undefined because JavaScript coerces booleans to be numbers when types don't match

false == null is equivalent to 0 === null for the same reason.

null == undefined is true because the spec says so.

Thanks for asking this question. My understanding of == is much better for having researched it.

smfoote
  • 5,449
  • 5
  • 32
  • 38
4

You can actually write a JavaScript function that behaves exactly like == that should give you some insight into how it behaves.

To show you what I mean here is that function:

// loseEqual() behaves just like `==`
function loseEqual(x, y) {
    // notice the function only uses "strict" operators 
    // like `===` and `!==` to do comparisons

    if(typeof y === typeof x) return y === x;

    if(typeof y === "function" || typeof x === "function") return false;

    // treat null and undefined the same
    var xIsNothing = (y === undefined) || (y === null);
    var yIsNothing = (x === undefined) || (x === null);

    if(xIsNothing || yIsNothing) return (xIsNothing && yIsNothing);

    if(typeof x === "object") x = toPrimitive(x);
    if(typeof y === "object") y = toPrimitive(y);

    if(typeof y === typeof x) return y === x;

    // convert x and y into numbers if they are not already use the "+" trick
    if(typeof x !== "number") x = +x;
    if(typeof y !== "number") y = +y;

    return x === y;
}

function toPrimitive(obj) {
    var value = obj.valueOf();
    if(obj !== value) return value;
    return obj.toString();
}

As you can see == has a lot of complicated logic for type conversion. Because of that it's hard to predict what result you are going to get.

Here are some examples of some results you wouldn't expect:

Unexpected Truths

[1] == true // returns true
'0' == false // returns true
[] == false // returns true
[[]] == false // returns true
[0] == false // returns true

'\r\n\t' == 0 // returns true

Unexpected Conclusions

// IF an empty string '' is equal to the number zero (0)
'' == 0 // return true

// AND the string zero '0' is equal to the number zero (0)
'0' == 0 // return true

// THEN an empty string must be equal to the string zero '0'
'' == '0' // returns **FALSE**

Objects with Special Functions

// Below are examples of objects that
// implement `valueOf()` and `toString()`

var objTest = {
    toString: function() {
        return "test";
    }
};

var obj100 = {
    valueOf: function() {
        return 100;
    }
};

var objTest100 = {
    toString: function() {
        return "test";
    },
    valueOf: function() {
        return 100;
    }
};

objTest == "test" // returns true
obj100 == 100 // returns true
objTest100 == 100 // returns true

objTest100 == "test" // returns **FALSE**
Luis Perez
  • 27,650
  • 10
  • 79
  • 80