Wednesday, November 25, 2009

JavaScript's Evil Equality Twins (Equality vs Identity)

In JavaScript, there exists two sets of equality operators:

=== and !==

and, as Douglas Crockford likes to call them, their corresponding evil twins :

== and !=

What's the difference?

The first set of operators, called Identity Operators ( === and !== ), produce true (or false) whenever the given operands are both of the same type and have the same value.

On the other hand, the evil twins, formally known as Equality Operators ( == and != ), do type-coercion whenever the operands are of a different type before checking for value equality.

Type-Coercion?

First of all, don't get thrown off by the word coercion; it's just another word for conversion.
In Computer Science, there are two forms of type conversions, implicit and explicit.  The term used for implicit type conversion is coercion and the term used for explicit type conversion is casting.

In most languages, explicit casting is done via the parenthesis:

int main () {
    float fl = 7.2;
    int n = (int) fl;   // (int) makes an explicit cast
    //n is now 7
}

But JavaScript, being a loosely typed language, never casts.

Implicit casting, coercion, is the automatic type conversion that is done internally by the compiler.  For example, we can rewrite the above C code as such:

int main () {
    float fl = 7.2;
    int n = fl;   //now the C compiler is internally coercing the operator to an integer 
    //n is now 7
}

This time, the compiler is implicitly casting fl to an integer.

What's wrong with type-coercion ?

Earlier on I said that the evil twins do type-coercion before checking for type-equality, and unfortunately the rules by which they coerce the values are complicated and unmemorable.

Here are two cases of comparing the values with both sets:

//Using the equality operator (==)

true == 1; //true, because the boolean value true is converted to 1 and then compared
"2"  == 2  //true, because 2 is converted to "2" and then compared


//Using the identity operator (===)

true === 1  //false
"2"  === 2  // false

Here is a list of other "interesting" cases:

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

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

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

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

All of the comparisons shown above produce false with the === operator.

Therefore in my opinion, you should never use the Equality operators, but only use the Identity operators, === and !==

Simple enough?

Well, not really...because as usual, there has to be a caveat. Take a look at the following example:

var x = new String('hello');
var y = 'hello';

console.log(x === y); //shows false
console.log(x == y);  //shows true

As you can see, a false is returned when we used the Identity operator! This is because:

console.log(typeof x); // 'object'
console.log(typeof y); // 'string'

When we used the Identity operator ===, it returned false because x and y are not of the same type! This is due to the fact we used the constructor to initialize a string (new String()), the primitive value was boxed into an Object.

The moral of this is to Always use literals to initialize values and this goes for all the primitive types. I will leave further explanation of this for another post.