- Item 1: Know Which JavaScript You Are Using
- Item 2: Understand JavaScript's Floating-Point Numbers
- Item 3: Beware of Implicit Coercions
- Item 4: Prefer Primitives to Object Wrappers
- Item 5: Avoid using == with Mixed Types
- Item 6: Learn the Limits of Semicolon Insertion
- Item 7: Think of Strings As Sequences of 16-Bit Code Units
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 = newDate
(); 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 = newDate
(); 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 = newDate
(); 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 = newDate
("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:
functiontoYMD
(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.