One of the first face-palm moments I had with Javascript was with the equality operators. Or what I thought were the equality operators. Because, you see, in C++ and C#, == means "is equal to" and != means "is not equal to". And they are type-safe, so you can't easily shoot yourself in the foot (unless you overload them).
So you happily start using them churning out validation code until you realize there is something wrong - comparisons sometimes work, but sometimes don't. And they are not always transitive. What's is going on?
First, you have to be sure that you are comparing apples to apples. Are you really comparing numbers to numbers? Look closer:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var userEntered = '0'; | |
var limit = 0; | |
if (userEntered == limit) { | |
// passes | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var userEntered = '0.0'; | |
var limit = 0; | |
if (userEntered == limit) { | |
// still works | |
} |
Turns out there is. Because these two operators are not testing equality - instead they are testing equivalence.
In general numbers are equivalent to their string representation (as is NaN), but dates (Date objects) are not. null is equivalent to undefined but to nothing else.
Now, if I want true equality, there is one solution - the triple equal (===) and its triple non-equal buddy (!==). The previous examples then become
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var userEntered = '0.0'; | |
var limit = 0; | |
if (userEntered === limit) { | |
// nope | |
} | |
if (userEntered !== limit) { | |
// yup | |
} | |
// but also... | |
userEntered = 0.0; | |
if (userEntered === limit) { | |
// nope | |
} |
- you are comparing the right things (numbers to numbers)
- you are comparing what matters (actual value vs string representation).
Rules of thumb to decide which one to use (work for me, feel free to use them or leave them)
- Good candidates for equivalence are cases where type conversion does not matter or is even welcome. Think integer-to-float comparisons. null-to-undefined is also a good candidate.
- Think hard what kind of input you receive in your routines. If it is whatever jQuery's val() is returning, you are better off parsing it to the target type first and then deciding which one of the two approaches is best.
- In general avoid comparing anything to strings (unless it is also a string).
A lot of bugs and confusion can be avoided if you remember - equivalence is powerful, but it is not transitive. If a == b and b == c, it does not mean that a == c.
Equality is strict, but it is often more straight-forward, with less nasty surprises. And yes, if a === b and b === c, you are guaranteed that a === c.
No comments:
Post a Comment