2011-12-02

When is it OK to use == in JavaScript?

Update 2011-12-07: Added case 5 and a conclusion.

Short answer: never. This post looks at five possible exemptions from the rule to always use === and explains why they aren’t.

JavaScript has two operators for determining whether two values are equal [1]:

  • The strict equality operator === only considers values equal that have the same type.
  • The “normal” (or lenient) equality operator == tries to convert values of different types, before comparing like strict equality.
The advice given to JavaScript beginners is to completely forget about == and to always use ===. It turns out that that rule is universally true. There are five cases that look like exemptions from that rule, but they really aren’t. The guiding principle I use from now on is:
Prefer intention-revealing code to terse code.
Remember: Your code is only written once but likely read many times – make it as easy for readers as possible.

Case 1: You know what you are comparing with

For example, with the typeof operator [2], you can be sure that the result will be a string. Then == is safe to use, because we can be sure that it won’t perform any conversion shenanigans:
    if (typeof x == "function") {
        ...
    }
However, there are two reasons not to do this:
  • Consistency: You don’t get any benefits from using == here, so why deviate from the simple rule of avoiding it?
  • Simplicity and performance: In general, === is the simpler operation, because it doesn’t convert its operands. The performance landscape across JavaScript engines is uneven [3], but on most browsers === is as fast or faster than ==.

Case 2: comparing with undefined and null

With ==, undefined and null are in the same equivalence class – they are equal to themselves and to each other, but to nothing else (including values that are considered false in JavaScript):
    > null == null
    true
    > undefined == null
    true
    > false == null
    false
    > 0 == null
    false
Thus, the following if statement checks for either null or undefined.
    if (x == null) {
        ...
    }
However, the increased terseness is offset by such code being less explicit about your intentions: If the check for undefined matters then you might as well put that in writing. Otherwise, if a JavaScript beginner reads your code, they might think you are only checking for null. If an advanced programmer reads your code, they might think that you made an error and meant to write ===.
    if (x === undefined || x === null) {
        ...
    }
If you can afford to be a little sloppy then the above can be abbreviated to
    if (!x) {
        ...
    }
The usual caveat applies: The condition holds if x has either of the following “falsy” values.
    undefined
    null
    false
    0
    ""

Case 3: comparing strings and numbers

Scenario: You are working with user interface code or code that handles server-side parameters. Then you are likely to get handed numbers encoded as strings. If x is such a string, you can compare it via
    if (x == 123) {
        ...
    }
But why not tell readers of your code that if x isn’t a number, it should be converted to one?
    if (Number(x) === 123) {
        ...
    }

Case 4: comparing objects to primitives

With == you can compare a primitive to either a primitive or an instance of a wrapper type [4]:
    > function isAbc(x) { return x == "abc" }
    > isAbc("abc")
    true
    > isAbc(new String("abc"))
    true
With ===, you can’t:
    > new String("abc") === "abc"
    false
The left-hand side is an object while the right-hand side is a primitive. Hence, they don’t have the same type and aren’t strictly equal. However, you again should make it a priority to explain to a reader of your code what your intentions are. Given the expression
    x == "abc"
What do you want to accomplish?
  • Is it really about comparing either a wrapped string or a string to the right-hand side? It seems unlikely, but if so, you should be very careful about documenting what is going on.
  • Do you want to convert x to a string? Then you can write more explicitly
        String(x) === "abc"
    
  • Do you want to extract a wrapped primitive? Then you should consider
        x.valueOf() === "abc"
    

Case 5: JavaScript is flexible – my code should be, too

The argument goes like this: I want my code to exhibit the same flexibility that JavaScript normally does. And == helps me with that. Examples of where JavaScript is flexible, it automatically converts values:
    > "abc" + false
    'abcfalse'
    > 3 + true
    4
    > +"73"
    73
There are several counter-arguments to the above hypothesis:
  • Even the standard conversions might not always work the way you need them too. Examples:
        > !"false"
        false
        > 7 + "3"
        '73'
        > Number("")
        0
    
  • Lenient equality works differently from how conversions are normally performed:
        > 2 == false
        false
        > 2 == true
        false
        > Boolean(2)
        true
    
  • An explicit conversion plus strict equality results in more descriptive code. Compare: Flexibility via lenient equality.
        function is123Implicit(x) {
            return x == 123;
        }
        > is123Implicit(123)
        true
        > is123Implicit(new Number(123))
        true
        > is123Implicit("123")
        true
    
    Alternative: Flexibility via an explicit conversion and strict equals.
        function is123Explicit(x) {
            x = Number(x);
            return x === 123;
        }
        > is123Explicit(123)
        true
        > is123Explicit(new Number(123))
        true
        > is123Explicit("123")
        true
    
  • Who says your code has to be flexible? It can be argued that JavaScript’s default flexibility is a bug rather than a feature. Writing defensive code more quickly exposes bugs. A defensive version of is123Explicit() looks as follows:
        function is123Defensive(x) {
            if (typeof x !== "number") {
                throw new TypeError("Not a number: "+x);
            }
            return x === 123;
        }
    
    If you want to pass anything but a primitive number to this function, you must perform a conversion first.

Conclusion

I hope I have convinced you that sticking with the easy-to-follow rule “don’t use ==” makes sense, not just for newbies. Less magic in your code usually means that it is easier to understand.

Related reading

  1. Equality in JavaScript: === versus ==
  2. Improving the JavaScript typeof operator
  3. jsPerf: == versus ===
  4. JavaScript values: not everything is an object

No comments: