Comparisons
You already know most of these from a maths class. Is one number bigger than another? Are two values the same? JavaScript answers those questions with operators that look familiar, plus a few that will surprise you.
Here’s the full set:
- Greater than / less than:
a > b,a < b. - Greater than or equal / less than or equal:
a >= b,a <= b. - Equal:
a == b. Watch the doubled sign.==asks a question (“are these equal?”), while a singlea = bassigns a value. Mixing them up is one of the most common beginner bugs. - Not equal: maths writes
≠, JavaScript writesa != b.
Most of this behaves the way your intuition expects. The interesting parts are the edges: comparing strings, comparing values of different types, and the strange things that null and undefined do. By the end you’ll have a short rule that sidesteps nearly all of the weirdness.
Every comparison gives back a boolean
Run any comparison and you get one of exactly two values:
true— the statement holds.false— it doesn’t.
Nothing else. There’s no “maybe.”
alert( 2 > 1 ); // true
alert( 2 == 1 ); // false
alert( 2 != 1 ); // true
Because the result is an ordinary value, you can store it, pass it around, or print it like any other:
let result = 5 > 4; // the comparison runs first, then the result is stored
alert( result ); // true
Comparing strings
Strings don’t have a numeric size, so JavaScript compares them the way a dictionary orders words: character by character, from the start. This is called lexicographical order.
alert( 'M' > 'D' ); // true
alert( 'Snow' > 'Snap' ); // true
alert( 'Star' > 'Sta' ); // true
The algorithm is short:
- Compare the first character of each string.
- If they differ, whichever character is “greater” decides the whole comparison. Done.
- If they’re equal, move to the second character and repeat.
- Keep going until one string runs out of characters.
- If both end together, the strings are equal. If one ran out first, the shorter one is “less.”
Walk through the three examples. 'M' > 'D' is settled on step one — M sits after D. 'Snow' versus 'Snap' takes longer:
And 'Star' > 'Sta': the first three characters match, then the first string still has an r while the second has nothing left. The longer string wins, so 'Star' is greater.
Type two strings and watch the comparison walk character by character until it finds the first difference — exactly the algorithm above.
Comparing different types
Give >, <, >=, <=, or == two operands of different types and JavaScript doesn’t refuse. It first converts the values to numbers, then compares those.
alert( '3' > 2 ); // true — the string '3' becomes the number 3
alert( '05' == 5 ); // true — '05' becomes 5
Booleans convert too: true turns into 1 and false into 0.
alert( true == 1 ); // true
alert( false == 0 ); // true
Strict equality
Regular == has a blind spot: after converting to numbers, it can’t tell 0 apart from false.
alert( 0 == false ); // true
The empty string has the same problem, because it also converts to zero:
alert( '' == false ); // true
That’s the type conversion at work — both false and '' collapse to 0, so they match. Often you don’t want that. You want 0 (a real number) to stay distinct from false (a boolean) and from '' (an empty string).
The strict equality operator === compares without any type conversion.
If a and b are different types, a === b returns false on the spot — no attempt to convert, no coercion.
alert( 0 === false ); // false — number vs boolean, different types
There’s a matching strict non-equality operator !==, the strict version of !=.
Yes, === is one character longer. But it says exactly what it means and removes a whole category of surprises. A good default: reach for === and !==, and only drop to == when you deliberately want the loose behavior.
Pick any two values and see both operators side by side. Notice how often == says true where === says false — that gap is the type conversion happening quietly behind ==.
Comparing with null and undefined
Here’s where intuition breaks down. null and undefined follow special rules, and they differ depending on which operator you use.
null === undefined is falsenull == undefined is true, and neither equals anything elsenull becomes 0, undefined becomes NaNBroken out:
Strict equality ===. No conversion happens, and null and undefined are distinct types, so they’re not equal:
alert( null === undefined ); // false
Loose equality ==. A dedicated rule makes them a matched pair: null and undefined equal each other and nothing else. Not 0, not false, not ''.
alert( null == undefined ); // true
Ordering < > <= >=. These convert to numbers. null becomes 0; undefined becomes NaN (Not-a-Number), which loses every comparison it’s part of.
These rules interact in ways that trip people up. Two examples show the traps, and then a rule to stay clear of them.
The null vs 0 puzzle
alert( null > 0 ); // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true
Read those three together and it looks impossible. Line 3 says null is greater-than-or-equal to 0, yet line 1 says it isn’t greater, and line 2 says it isn’t equal either. If it’s neither greater nor equal, how can it be “greater or equal”?
The answer: lines 1 and 3 don’t play by the same rules as line 2.
>and>=are ordering operators, so they convertnullto the number0. Then0 > 0isfalse(line 1) but0 >= 0istrue(line 3).==uses the special couple rule instead. It never convertsnullto a number, andnullonly matchesnullorundefined— not0. So line 2 isfalse.
null > 0→ convert →0 > 0→falsenull == 0→ couple rule, no convert →falsenull >= 0→ convert →0 >= 0→trueundefined can’t be compared
undefined is even more antisocial. It loses every comparison:
alert( undefined > 0 ); // (1) false
alert( undefined < 0 ); // (2) false
alert( undefined == 0 ); // (3) false
Not greater, not less, not equal. Why does it hate zero this much?
- Lines 1 and 2 convert
undefinedtoNaN. AndNaNis a special numeric value that returnsfalseagainst everything, in every direction — evenNaN < 0andNaN > 0are bothfalse. - Line 3 uses the couple rule.
undefinedonly equalsnulland itself, so comparing it to0givesfalse.
How to avoid the whole mess
You don’t need to memorize these edge cases. They’ll sink in with time, but there’s a reliable habit that keeps you out of trouble:
- Treat any comparison involving
nullorundefinedas suspect — except a strict=== nullor=== undefined, which is safe and clear. - Never use
>,<,>=, or<=on a variable that might benullorundefined, unless you’ve thought it through carefully. If a variable could hold those values, check for them on their own line first.
Run the naive check against the guarded one. The null and undefined cases are where they disagree — and where the naive version quietly does the wrong thing.
Summary
- Every comparison operator returns a boolean,
trueorfalse. - Strings compare lexicographically — character by character, using Unicode order, so case matters.
- When the operands are different types, they convert to numbers first. The exception is strict equality
===, which never converts. nullandundefinedequal each other and themselves under==, but match no other value. Under===they’re distinct. Under ordering operators they convert to0andNaN.- Prefer
===and!==so type conversion never surprises you. - Be wary of
>,<,>=,<=on any variable that could benullorundefined. Check for those cases separately.