The Curious Schemer

The following sentence is false. The preceding sentence is true.

Really Understanding JavaScript’s Equality and Identity

with 12 comments

=== and !==. First time somebody told me about these two JavaScript operators, I thought she was pulling my legs. I was oblivious to these two operators for the longest time–after all, my JavaScript code had been working just fine with == and !=.

It was not until I found Douglas Crockford’s JSLint that I started wondering about these funny operators. Because JSLint just. Won’t. STOP. COMPLAINING. About almost every single use of == and !=. It gives out error messages like this:

Problem at line 3 character 11: Use '===' to compare with 'null'.

if(id == null) {

Very annoying. But I thought, hey, if Douglas Crockford says so, maybe there’s a good reason behind it, no? So I whipped out my trusty JavaScript: The Definitive Guide to find out more about these operators. (Come on, when was the last time you’re learning a new language and read the operators section?)

Here’s the difference between == and === : == is an operator to check for equality, whereas === is an operator to check for identity. What’s the difference? Well, if you come from a language like Java or C#, you know that just because two objects are equal doesn’t mean they’re identical. That’s what equals() is for–to find out whether two different (that is, nonidentical) objects are equal. Likewise,!= is a inequality operator, and !== is a nonidentity operator.

Fine, that was easy enough. But it ain’t helping. So what? Why is JSLint complaining about x == null? What’s the difference between 3 == 3 and 3 === 3?

The Silent Type Conversion

I dug deeper, and I found something that programmers coming from other languages may find surprising. Examples will illustrate this best (I’m using the jrunscript console that I blogged about here):

js> null == undefined
true
js> null === undefined
false
js> 5 == "5"
true
js> 5 === "5"
false
js> "true" == true
false
js> 1 == true
true
js> "1" == true
true
js> 1 === true
false
js> "0" == false
true
js> "0" === false
false
js> var x = { id: 123, name: "Blah" };
js> var y = { id: 456, name: "Not Blah, definitely" };
js> x == "[object Object]"
true
js> y == "[object Object]"
true
js> x == y
false

So you see that == (and != too) do type conversion under the hood before testing for equality. This may give some very surprising result, such as this:

js> 1 != true
false
js> 0 != false
false
js> 5 != "5"
false
js> var x = new Object();
js> x != "[object Object]"
false

Ahhh so. I finally figured out why JSLint was so fanatical in complaining about == and !=. Because JavaScript does type conversion under the hood when it checks for equality, which may surprise the heck out of programmers from other languages! We can find what the conversions are from ECMA-262 specification… but why? David Flanagan has done it for us in JavaScript: The Definitive Guide. Specifically, the type conversions are:

  1. null and undefined are equal
  2. if a number is compared to a string, the string is converted to a number, and the comparison is done again
  3. true is converted to 1, false to 0
  4. if an object is compared to a number or string, the object is converted to primitive, either by calling its valueOf() or toString() method.

Both === and !== don’t do any of these conversions.

Know What You’re Doing

So what was Douglas Crockford trying to say when his JSLint balked at the use of == and !=? (The latest version seems to be more relaxed in detecting this.)

I believe his point is that we should know what we’re trying to do. Consider the following example:

function doSomething(id, name) {

if(id == null) {

alert("id is mandatory");

}

}

Do you really want to check whether id is null, and do something different if id is undefined? Or you don’t really care whether id is null or undefined, as long as it has some values? If it’s the former, you’ve gotta use ===. For the latter you use ==.

Another example is comparing the contents of an HTML text box with a number. Let’s say you have a textbox containing a person’s age. With ==, it’s convenient:

if(document.getElementById("age").value == 20)

Using === will be troublesome in this situation. So the moral of the story is: really know what you want. The silent type conversion is there to help you, to make things easier for you. But you have to know what’s happening under the hood so you won’t get nasty surprises for cases in which you really mean === (or !==).

About these ads

Written by rayfd

March 18, 2007 at 3:34 pm

Posted in JavaScript, Technology

12 Responses

Subscribe to comments with RSS.

  1. A bit of a discussion of the history of this operator is here, if you’re interested:

    http://blogs.msdn.com/ericlippert/archive/2004/07/26/197302.aspx

    Eric Lippert

    March 28, 2007 at 6:35 pm

  2. [...] is meant exactly for this kind of things. Unlike JavaScript, where the === operator is meant for identity, in Ruby, the === method is usually called in case expressions to match the case target for each [...]

  3. So how does Javascript actually do the test for identity? Is there some kind of hashCode value, or something, that provides the object’s address in memory? Does Javascript use that for code like this:

    myAssociateiveArray[myObject] = ‘hi';

    I.e., does it use myObject’s address for the array index, or does it run some kind of toString() on the object, and use the string as the index? (In which case, how do I code the toString() for my object?)

    Anonymous

    January 30, 2009 at 5:32 pm

    • No two objects will ever compare equal, unless the two references actually reference the same object:

      var a = { id: “foo” };
      var b = { id: “foo” };

      a == b; /// false
      a === b; /// false, just FYI

      amn

      May 28, 2010 at 1:32 pm

      • Likewise,

        var a = { id: “foo” };
        var b = a;

        a == b; /// true
        a === b; /// true

        amn

        May 28, 2010 at 1:33 pm

    • There is no implicit toString or valueOf calls being made. If you want to do these sort of things, you will need to explicitly compare the results yourself:

      function Color(value)
      {
      this.value = value;
      }

      Color.prototype.valueOf = function()
      {
      return this.value;
      };

      var a = new Color(0xff0000);
      var b = new Color(0xff0000);

      a == b; /// false
      a === b; /// false

      a == 0xff0000; /// true
      b == 0xff0000; /// true

      a.valueOf() == b.valueOf(); /// true
      a.valueOf() === b.valueOf(); /// true

      amn

      May 28, 2010 at 1:38 pm

      • And again,

        a === 0xff0000; ///false

        amn

        May 28, 2010 at 1:44 pm

    • @amn. The question is how it determines identity. Is there some kind of address, or address hash, that the === operator looks at? Thanks. :-)

      Anonymous

      January 23, 2012 at 4:15 pm

  4. Awesome article Ray! Cheers!

    Nik

    July 27, 2009 at 8:54 am

  5. Great article. You saw the warnings and researched it before changing them, which shows a great deal more foresight than I had when I saw the warnings, changed them, checked in the code, and broke our product in crazy mysterious ways.

    Go me.

    James Thigpen

    September 20, 2010 at 9:06 pm

  6. Thanks. Its really helpful.

    Vinoth Kumar

    February 19, 2013 at 6:58 pm

  7. Very helpful, thank you!

    lastMove

    April 8, 2014 at 9:28 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: