Javascript ‘==’ operator and indexOf failure

The other day my collegue, Steve Skrla, was having an infinite recursion issue caused by my javascript clone function. We discovered that the for some reason it was equating a string value with an array value. After a little more digging Steve found the culprit. Simply put, Javascript’s == operator is broken.

I did a little more digging and discovered exactly what Javascript is doing. It turns out that when comparing anything to a string, Javascript first runs a toString on the other item being compared.

[edit]

I should say that this functionality is defined in ECMA script.

From 11.9.3 The Abstract Equality Comparison Algorithm:


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

Even though this functionality is as-designed, I believe it is still a bug. I think both steps 20 and 21 should both return false. This is what most people expect and how other scripting languages work. Please see the comments for more discussion.

[/edit]

Consider the following:

[-]View Code JAVASCRIPT
var a = ["Jason"];
var b = [[["Jason"]]];
var c = {name:"Jason"};
 
alert(a == "Jason");
alert(b == "Jason");
alert(c == "[object Object]");

All of the above alerts will output true. Just to go one step further and show that == is inconsistent in its behavior take a look at the following example:

[-]View Code JAVASCRIPT
var c = {name:"Jason"};
alert(c == ["[object Object]"]);

Notice the difference in this example than the previous. I am comparing an array with an object instead of an object with a string. Here, == does a type check and the 2 objects are deemed not equal. == only calls the toString on objects when they are compared with a string.

IndexOf broken

Things can get a little precarious when using indexOf. The good news is that Firefox implements an array indexOf operation that works correctly. The bad news is that IE (at least) doesn’t implement indexOf at all. This means that those of us relying on libraries like JQuery, Prototype, ExtJS …etc have custom implementations of indexOf that look something like this:

[-]View Code JAVASCRIPT
indexOf : function(o){
   for (var i = 0, len = this.length; i < len; i++){
      if(this[i] == o) return i;
   }
   return -1;
}

Notice the reliance on the == operator. This means that our indexOf’s don’t really work:

[-]View Code JAVASCRIPT
//include ExtJS / Prototype ... etc
var c = [[["Jason"]]];
alert(c.indexOf("Jason"));

The above will output 0 because it equated “Jason” to [["Jason"]]. This is wrong.

The Fix

While we can’t fix the == we can fix our libraries’ indexOf implementations. All we need to do is use the === (triple equals) operator which does an additional typeof check internally:

[-]View Code JAVASCRIPT
indexOf : function(o){
   for (var i = 0, len = this.length; i < len; i++){
      if(this[i] === o) return i;
   }
   return -1;
}

Please help get this information out in the wild so library developers can get this fix in.

6 Responses to “Javascript ‘==’ operator and indexOf failure”


  1. 1 Juan Mendes

    I think calling your problem with the equals operator a bug is not accurate, even though that behavior is probably not what most programmers intended. The double equals operator is known to convert objects to strings when comparing them to other strings. We at workforce software have come to use strict equality in most cases to avoid problems like that.

  2. 2 Jason

    To say that == functionality is a bug I suppose can be considered incorrect only by the fact that this functionality is documented. But you hit on my point exactly; it’s not how most programmers expect it to behave. Good programming languages should be predictable and useful in the way they behave.

    You also said you take care to always use ‘===’ in most cases. ‘===’ is hardly the de facto standard equality comparison operator. Just by the fact that you have to consciously make a decision to use the triple equals to “avoid problems like [this]” indicates an inherent problem with the ‘==’ implementation. Simply put, it doesn’t do what you want nor expect. I call that a bug.

    The conversion of types functionality is not particularly useful in so far as comparing objects or functions to primitive types and strings. The only use for type conversion is in comparing a number to a string that contains a number: 6.2 == “6.2″ Therefore, I consider this a bug. Most, if not all other scripting languages, convert between primitives but do type checking for objects.

    Further supporting this as a bug is that no other language (to my knowledge… please correct me if I am wrong and I very well could be since I have not used every scripting language ;-)) behaves this way except for ECMA script.

    To suggest that this is common knowledge is definitely incorrect since every major javascript library currently implements indexOf using ‘==’ instead of ‘===’ which is incorrect.

    I am glad that you use ‘===’ and that you are aware of the pitfalls of ‘==’. However, I would venture to guess that 90% of developers out there do not know this and do not have any idea that the libraries they are using implement indexOf incorrectly.

  3. 3 Juan Mendes

    Point taken. I would have figured prototype, ext-js and jquery would know better than to use double equals for an equality test in a very generic function.

  4. 4 Jason

    Yeah I know… this bug is really pervasive across libraries and no one really notices! Here is the link to the bug report for ExtJS: http://extjs.com/forum/showthread.php?t=40040

  5. 5 Radomir

    wasn’t there a new release for this?

  6. 6 Rob

    Type coercion seems to be Bad, at least as far as Douglas Crockford is concerned. Check out http://www.jslint.com/lint.html (scroll down to “== and !=”).

Leave a Reply