Home > Articles > Open Source > Ajax & JavaScript

  • Print
  • + Share This
This chapter is from the book

Item 5: Avoid using == with Mixed Types

What would you expect to be the value of this expression?

"1.0e0" == { valueOf: function() { return true; } };

These two seemingly unrelated values are actually considered equivalent by the == operator because, like the implicit coercions described in Item 3, they are both converted to numbers before being compared. The string "1.0e0" parses as the number 1, and the object is converted to a number by calling its valueOf method and converting the result (true) to a number, which also produces 1.

It’s tempting to use these coercions for tasks like reading a field from a web form and comparing it with a number:

var today = new Date();

if (form.month.value == (today.getMonth() + 1) &&
    form.day.value == today.getDate()) {
    // happy birthday!
    // ...
}

But it’s actually easy to convert values to numbers explicitly using the Number function or the unary + operator:

var today = new Date();

if (+form.month.value == (today.getMonth() + 1) &&
    +form.day.value == today.getDate()) {
    // happy birthday!
    // ...
}

This is clearer, because it conveys to readers of your code exactly what conversion is being applied, without requiring them to memorize the conversion rules. An even better alternative is to use the strict equality operator:

var today = new Date();

if (+form.month.value === (today.getMonth() + 1) && // strict
    +form.day.value === today.getDate()) {          // strict
    // happy birthday!
    // ...
}

When the two arguments are of the same type, there’s no difference in behavior between == and ===. So if you know that the arguments are of the same type, they are interchangeable. But using strict equality is a good way to make it clear to readers that there is no conversion involved in the comparison. Otherwise, you require readers to recall the exact coercion rules to decipher your code’s behavior.

As it turns out, these coercion rules are not at all obvious. Table 1.1 contains the coercion rules for the == operator when its arguments are of different types. The rules are symmetric: For example, the first rule applies to both null == undefined and undefined == null. Most of the time, the conversions attempt to produce numbers. But the rules get subtle when they deal with objects. The operation tries to convert an object to a primitive value by calling its valueOf and toString methods, using the first primitive value it gets. Even more subtly, Date objects try these two methods in the opposite order.

Table 1.1. Coercion Rules for the == Operator

Argument Type 1

Argument Type 2

Coercions

null

undefined

None; always true

null or undefined

Any other than null or undefined

None; always false

Primitive string, number, or boolean

Date object

Primitive => number, Date object => primitive (try toString and then valueOf)

Primitive string, number, or boolean

Non-Date object

Primitive => number, non-Date object => primitive (try valueOf and then toString)

Primitive string, number, or boolean

Primitive string, number, or boolean

Primitive => number

The == operator deceptively appears to paper over different representations of data. This kind of error correction is sometimes known as “do what I mean” semantics. But computers cannot really read your mind. There are too many data representations in the world for JavaScript to know which one you are using. For example, you might hope that you could compare a string containing a date to a Date object:

var date = new Date("1999/12/31");
date == "1999/12/31"; // false

This particular example fails because converting a Date object to a string produces a different format than the one used in the example:

date.toString(); // "Fri Dec 31 1999 00:00:00 GMT-0800 (PST)"

But the mistake is symptomatic of a more general misunderstanding of coercions. The == operator does not infer and unify arbitrary data formats. It requires both you and your readers to understand its subtle coercion rules. A better policy is to make the conversions explicit with custom application logic and use the strict equality operator:

function toYMD(date) {
    var y = date.getYear() + 1900, // year is 1900-indexed
        m = date.getMonth() + 1,   // month is 0-indexed
        d = date.getDate();
    return y
         + "/" + (m < 10 ? "0" + m : m)
         + "/" + (d < 10 ? "0" + d : d);
}
toYMD(date) === "1999/12/31"; // true

Making conversions explicit ensures that you don’t mix up the coercion rules of ==, and—even better—relieves your readers from having to look up the coercion rules or memorize them.

Things to Remember

  • The == operator applies a confusing set of implicit coercions when its arguments are of different types.
  • Use === to make it clear to your readers that your comparison does not involve any implicit coercions.
  • Use your own explicit coercions when comparing values of different types to make your program’s behavior clearer.
  • + Share This
  • 🔖 Save To Your Account