|
| 1 | +# Comparisons |
| 2 | + |
| 3 | +We know many comparison operators from maths. |
| 4 | + |
| 5 | +In JavaScript they are written like this: |
| 6 | + |
| 7 | +- Greater/less than: <code>a > b</code>, <code>a < b</code>. |
| 8 | +- Greater/less than or equals: <code>a >= b</code>, <code>a <= b</code>. |
| 9 | +- Equals: `a == b`, please note the double equality sign `==` means the equality test, while a single one `a = b` means an assignment. |
| 10 | +- Not equals. In maths the notation is <code>≠</code>, but in JavaScript it's written as <code>a != b</code>. |
| 11 | + |
| 12 | +In this article we'll learn more about different types of comparisons, how JavaScript makes them, including important peculiarities. |
| 13 | + |
| 14 | +At the end you'll find a good recipe to avoid "JavaScript quirks"-related issues. |
| 15 | + |
| 16 | +## Boolean is the result |
| 17 | + |
| 18 | +All comparison operators return a boolean value: |
| 19 | + |
| 20 | +- `true` -- means "yes", "correct" or "the truth". |
| 21 | +- `false` -- means "no", "wrong" or "not the truth". |
| 22 | + |
| 23 | +For example: |
| 24 | + |
| 25 | +```js run |
| 26 | +alert( 2 > 1 ); // true (correct) |
| 27 | +alert( 2 == 1 ); // false (wrong) |
| 28 | +alert( 2 != 1 ); // true (correct) |
| 29 | +``` |
| 30 | + |
| 31 | +A comparison result can be assigned to a variable, just like any value: |
| 32 | + |
| 33 | +```js run |
| 34 | +let result = 5 > 4; // assign the result of the comparison |
| 35 | +alert( result ); // true |
| 36 | +``` |
| 37 | + |
| 38 | +## String comparison |
| 39 | + |
| 40 | +To see whether a string is greater than another, JavaScript uses the so-called "dictionary" or "lexicographical" order. |
| 41 | + |
| 42 | +In other words, strings are compared letter-by-letter. |
| 43 | + |
| 44 | +For example: |
| 45 | + |
| 46 | +```js run |
| 47 | +alert( 'Z' > 'A' ); // true |
| 48 | +alert( 'Glow' > 'Glee' ); // true |
| 49 | +alert( 'Bee' > 'Be' ); // true |
| 50 | +``` |
| 51 | + |
| 52 | +The algorithm to compare two strings is simple: |
| 53 | + |
| 54 | +1. Compare the first character of both strings. |
| 55 | +2. If the first character from the first string is greater (or less) than the other string's, then the first string is greater (or less) than the second. We're done. |
| 56 | +3. Otherwise, if both strings' first characters are the same, compare the second characters the same way. |
| 57 | +4. Repeat until the end of either string. |
| 58 | +5. If both strings end at the same length, then they are equal. Otherwise, the longer string is greater. |
| 59 | + |
| 60 | +In the first example above, the comparison `'Z' > 'A'` gets to a result at the first step. |
| 61 | + |
| 62 | +The second comparison `'Glow'` and `'Glee'` needs more steps as strings are compared character-by-character: |
| 63 | + |
| 64 | +1. `G` is the same as `G`. |
| 65 | +2. `l` is the same as `l`. |
| 66 | +3. `o` is greater than `e`. Stop here. The first string is greater. |
| 67 | + |
| 68 | +```smart header="Not a real dictionary, but Unicode order" |
| 69 | +The comparison algorithm given above is roughly equivalent to the one used in dictionaries or phone books, but it's not exactly the same. |
| 70 | +
|
| 71 | +For instance, case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? The lowercase `"a"`. Why? Because the lowercase character has a greater index in the internal encoding table JavaScript uses (Unicode). We'll get back to specific details and consequences of this in the chapter <info:string>. |
| 72 | +``` |
| 73 | + |
| 74 | +## Comparison of different types |
| 75 | + |
| 76 | +When comparing values of different types, JavaScript converts the values to numbers. |
| 77 | + |
| 78 | +For example: |
| 79 | + |
| 80 | +```js run |
| 81 | +alert( '2' > 1 ); // true, string '2' becomes a number 2 |
| 82 | +alert( '01' == 1 ); // true, string '01' becomes a number 1 |
| 83 | +``` |
| 84 | + |
| 85 | +For boolean values, `true` becomes `1` and `false` becomes `0`. |
| 86 | + |
| 87 | +For example: |
| 88 | + |
| 89 | +```js run |
| 90 | +alert( true == 1 ); // true |
| 91 | +alert( false == 0 ); // true |
| 92 | +``` |
| 93 | + |
| 94 | +````smart header="A funny consequence" |
| 95 | +It is possible that at the same time: |
| 96 | +
|
| 97 | +- Two values are equal. |
| 98 | +- One of them is `true` as a boolean and the other one is `false` as a boolean. |
| 99 | +
|
| 100 | +For example: |
| 101 | +
|
| 102 | +```js run |
| 103 | +let a = 0; |
| 104 | +alert( Boolean(a) ); // false |
| 105 | +
|
| 106 | +let b = "0"; |
| 107 | +alert( Boolean(b) ); // true |
| 108 | +
|
| 109 | +alert(a == b); // true! |
| 110 | +``` |
| 111 | +
|
| 112 | +From JavaScript's standpoint, this result is quite normal. An equality check converts values using the numeric conversion (hence `"0"` becomes `0`), while the explicit `Boolean` conversion uses another set of rules. |
| 113 | +```` |
| 114 | + |
| 115 | +## Strict equality |
| 116 | + |
| 117 | +A regular equality check `==` has a problem. It cannot differentiate `0` from `false`: |
| 118 | + |
| 119 | +```js run |
| 120 | +alert( 0 == false ); // true |
| 121 | +``` |
| 122 | + |
| 123 | +The same thing happens with an empty string: |
| 124 | + |
| 125 | +```js run |
| 126 | +alert( '' == false ); // true |
| 127 | +``` |
| 128 | + |
| 129 | +This happens because operands of different types are converted to numbers by the equality operator `==`. An empty string, just like `false`, becomes a zero. |
| 130 | + |
| 131 | +What to do if we'd like to differentiate `0` from `false`? |
| 132 | + |
| 133 | +**A strict equality operator `===` checks the equality without type conversion.** |
| 134 | + |
| 135 | +In other words, if `a` and `b` are of different types, then `a === b` immediately returns `false` without an attempt to convert them. |
| 136 | + |
| 137 | +Let's try it: |
| 138 | + |
| 139 | +```js run |
| 140 | +alert( 0 === false ); // false, because the types are different |
| 141 | +``` |
| 142 | + |
| 143 | +There is also a "strict non-equality" operator `!==` analogous to `!=`. |
| 144 | + |
| 145 | +The strict equality operator is a bit longer to write, but makes it obvious what's going on and leaves less room for errors. |
| 146 | + |
| 147 | +## Comparison with null and undefined |
| 148 | + |
| 149 | +There's a non-intuitive behavior when `null` or `undefined` are compared to other values. |
| 150 | + |
| 151 | +For a strict equality check `===` |
| 152 | +: These values are different, because each of them is a different type. |
| 153 | + |
| 154 | + ```js run |
| 155 | + alert( null === undefined ); // false |
| 156 | + ``` |
| 157 | + |
| 158 | +For a non-strict check `==` |
| 159 | +: There's a special rule. These two are a "sweet couple": they equal each other (in the sense of `==`), but not any other value. |
| 160 | + |
| 161 | + ```js run |
| 162 | + alert( null == undefined ); // true |
| 163 | + ``` |
| 164 | + |
| 165 | +For maths and other comparisons `< > <= >=` |
| 166 | +: `null/undefined` are converted to numbers: `null` becomes `0`, while `undefined` becomes `NaN`. |
| 167 | + |
| 168 | +Now let's see some funny things that happen when we apply these rules. And, what's more important, how to not fall into a trap with them. |
| 169 | + |
| 170 | +### Strange result: null vs 0 |
| 171 | + |
| 172 | +Let's compare `null` with a zero: |
| 173 | + |
| 174 | +```js run |
| 175 | +alert( null > 0 ); // (1) false |
| 176 | +alert( null == 0 ); // (2) false |
| 177 | +alert( null >= 0 ); // (3) *!*true*/!* |
| 178 | +``` |
| 179 | + |
| 180 | +Mathematically, that's strange. The last result states that "`null` is greater than or equal to zero", so in one of the comparisons above it must be `true`, but they are both false. |
| 181 | + |
| 182 | +The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, treating it as `0`. That's why (3) `null >= 0` is true and (1) `null > 0` is false. |
| 183 | + |
| 184 | +On the other hand, the equality check `==` for `undefined` and `null` is defined such that, without any conversions, they equal each other and don't equal anything else. That's why (2) `null == 0` is false. |
| 185 | + |
| 186 | +### An incomparable undefined |
| 187 | + |
| 188 | +The value `undefined` shouldn't be compared to other values: |
| 189 | + |
| 190 | +```js run |
| 191 | +alert( undefined > 0 ); // false (1) |
| 192 | +alert( undefined < 0 ); // false (2) |
| 193 | +alert( undefined == 0 ); // false (3) |
| 194 | +``` |
| 195 | + |
| 196 | +Why does it dislike zero so much? Always false! |
| 197 | + |
| 198 | +We get these results because: |
| 199 | + |
| 200 | +- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN` and `NaN` is a special numeric value which returns `false` for all comparisons. |
| 201 | +- The equality check `(3)` returns `false` because `undefined` only equals `null`, `undefined`, and no other value. |
| 202 | + |
| 203 | +### Avoid problems |
| 204 | + |
| 205 | +Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to avoid problems with them: |
| 206 | + |
| 207 | +- Treat any comparison with `undefined/null` except the strict equality `===` with exceptional care. |
| 208 | +- Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately. |
| 209 | + |
| 210 | +## Summary |
| 211 | + |
| 212 | +- Comparison operators return a boolean value. |
| 213 | +- Strings are compared letter-by-letter in the "dictionary" order. |
| 214 | +- When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check). |
| 215 | +- The values `null` and `undefined` equal `==` each other and do not equal any other value. |
| 216 | +- Be careful when using comparisons like `>` or `<` with variables that can occasionally be `null/undefined`. Checking for `null/undefined` separately is a good idea. |
0 commit comments