Skip to content

Operators and comparisons

docs.scrimba.com

Say you are tracking a shopping cart. You add up prices, apply a discount, check whether the total crosses a free-shipping threshold, and decide whether to show a warning when the cart is empty. Every one of those steps is an operator at work: a symbol that takes one or two values and produces a new one. This chapter covers the operators you reach for constantly, from doing arithmetic on numbers to comparing values and combining true-or-false tests.

Arithmetic and assignment

The arithmetic operators do the maths you already know: + adds, - subtracts, * multiplies, / divides. There is one more you might not have met: %, the remainder operator, which gives you what is left over after a division.

js
const total = 8 + 5; // 13
const change = 20 - 13; // 7
const leftover = 17 % 5; // 2, because 5 goes into 17 three times with 2 left
console.log(total); // 13

When you want to update a value based on itself, there is a shorthand. Instead of writing score = score + 10, you can write score += 10. It means the same thing: take the current value, add 10, store the result back.

js
let score = 100;
score += 10; // score is now 110
score -= 30; // score is now 80
console.log(score); // 80

And to move a number up or down by exactly one, ++ and -- do it in a single step. count++ adds one, count-- takes one away.

js
let lives = 3;
lives--; // lives is now 2
console.log(lives); // 2

The arithmetic operators are +, -, *, /, and %. The first four are familiar; % is the one worth a second look. It returns the remainder of a division, which makes it the standard tool for "is this evenly divisible?" checks and for wrapping a value back around within a range.

js
const isEven = 10 % 2; // 0, so 10 is even
const isOdd = 7 % 2; // 1, so 7 is odd
console.log(isEven); // 0

Updating a value from itself is common enough to have shorthands. Each arithmetic operator pairs with = to form a compound assignment: +=, -=, *=, /=, %=. So price *= 1.2 reads as "multiply price by 1.2 and store it back", saving you from writing price = price * 1.2.

js
let price = 40;
price *= 1.2; // add 20 percent, price is now 48
price -= 8; // price is now 40
console.log(price); // 40

For a change of exactly one, ++ and -- increment and decrement in place. They read cleanly when you are stepping a counter, and you will see them constantly.

js
let attempts = 0;
attempts++; // attempts is now 1
console.log(attempts); // 1

The arithmetic operators hold few surprises for the four basics, so the two worth real attention are % and the + overload. The remainder operator % returns what is left after integer-style division, and it keeps the sign of the left operand: -7 % 3 is -1, not 2, which trips people who expect a mathematical modulo. It is your go-to for divisibility tests and for wrapping an index back into a range.

js
const remainder = 17 % 5; // 2
const negative = -7 % 3; // -1, sign follows the left operand
console.log(negative); // -1

The + operator is overloaded: with two numbers it adds, but if either side is a string it concatenates, coercing the other side to a string. That single rule is behind a whole class of surprises, so name what you have before you add it.

js
const sum = 5 + 3; // 8
const glued = "5" + 3; // "53", the 3 became a string
console.log(glued); // 53

Compound assignment (+=, -=, *=, /=, %=) is the idiomatic way to update a binding from itself, and it inherits the same + overload: msg += count stringifies count. The increment and decrement operators ++ and -- come in prefix and postfix forms that differ in the value the expression returns: x++ evaluates to the old value then increments, ++x increments then evaluates to the new one. In an expression that distinction bites, so keep them on their own statement where the difference does not matter. Use let for anything you reassign this way, since const bindings cannot be updated.

js
let counter = 5;
const before = counter++; // before is 5, counter is now 6
const after = ++counter; // after is 7, counter is now 7
console.log(`${before} ${after}`); // 5 7
JunoArithmetic and assignment The maths operators are the ones you expect, plus % for the remainder after dividing. When you update a value from itself, reach for the shorthand: score += 10 instead of score = score + 10. And ++ or -- nudges a number up or down by one. Took me a while to trust these, but they are the same maths written shorter.
JunoArithmetic and assignment Five arithmetic operators, and % for the remainder is the one that earns its keep in divisibility checks. Compound assignment like price *= 1.2 updates a value from itself in one move, and ++ or -- steps by one. Use let for anything you plan to update this way, since a const cannot be reassigned.
JunoArithmetic and assignment Two things to keep sharp: % keeps the sign of the left operand, so -7 % 3 is -1, and + concatenates the moment either side is a string. Prefix versus postfix ++ return different values, so keep them on their own statement where it cannot bite. The rest of the arithmetic operators behave exactly as you would guess.

Comparisons

A comparison asks a question about two values and answers with true or false. Those true-or-false answers are booleans, and they are what your program uses to make decisions later on.

The comparison you will use most is ===, which asks "are these two exactly equal?"

js
const answer = 42;
console.log(answer === 42); // true
console.log(answer === 7); // false

Its opposite is !==, which asks "are these two different?" And for order there are <, >, <=, and >=, which work as they do in maths: less than, greater than, less than or equal, greater than or equal.

js
const age = 18;
console.log(age >= 18); // true
console.log(age !== 21); // true

Always compare with ===, never ==. The double-equals version has a quirk that surprises people, and the triple-equals version does what you expect every time.

A comparison takes two values and returns a boolean, true or false. That boolean is the raw material for the decisions you will write in conditionals, so getting comparison right matters.

Use === for equality and !== for inequality. For ordering there are <, >, <=, and >=.

js
const stock = 3;
console.log(stock === 0); // false
console.log(stock > 0); // true
console.log(stock <= 5); // true

The reason to reach for === rather than == is that they are not the same operator. === (strict equality) compares both value and type, returning true only when they match. == (loose equality) first converts the two sides to a common type and then compares, which produces results almost nobody predicts.

js
console.log(0 == ""); // true, and rarely what you want
console.log("1" == 1); // true, the string became a number
console.log("1" === 1); // false, different types

Default to === and !== every time. They compare without converting, so you never have to reason about the conversion rules. Save == for reading old code, not writing new code.

A comparison returns a boolean, and the operators split into two families: equality (===, !==) and relational (<, >, <=, >=). The relational ones are straightforward on numbers. The equality ones are where the real decision lives, because JavaScript gives you two of them.

=== (strict equality) returns true only when both operands share a type and a value, with no conversion. == (loose equality) runs an abstract-equality algorithm that coerces the operands toward a common type first, and the practical rules are worth knowing so you can read old code: comparing a number and a string converts the string to a number ("1" == 1 is true); comparing a boolean to anything converts the boolean to a number first (true == 1, false == 0); null == undefined is true but neither equals anything else; and NaN equals nothing, not even itself.

js
console.log(0 == ""); // true, "" becomes 0
console.log("1" == 1); // true, "1" becomes 1
console.log(null == undefined); // true, a special case
console.log(NaN === NaN); // false, NaN is never equal to anything

Use === and !==; treat == as read-only knowledge. The takeaway is not to memorise every coercion path, it is to sidestep them: strict equality has no coercion, so its result reads straight off the operands. The one place == earns a mention is the null == undefined check, which some codebases use deliberately to catch both nullish values at once, but the nullish operators later in this chapter do that job more clearly.

JunoComparisons A comparison asks a yes-or-no question about two values and answers with true or false. Use === to check equality, !== for "different", and <, >, <=, >= for order. Keep to === and skip ==, because the double version has a surprise in it and the triple version behaves.
JunoComparisons Every comparison hands you a boolean, which is the fuel for the decisions you write next. === checks value and type together; == converts the sides first and produces oddities like 0 == "" being true. Default to === and !== so you never reason about conversion, and leave == for reading old code.
JunoComparisons Strict equality compares type and value with no coercion, so its answer reads straight off the operands. Loose == runs a conversion algorithm behind your back, which is why "1" == 1 is true and NaN equals nothing at all. Write === and !== and keep the coercion rules as read-only knowledge for old code.

Logical operators and truthiness

Often you need to combine more than one comparison. The logical operators let you do that: && (and), || (or), and ! (not).

&& is true only when both sides are true. || is true when at least one side is true. ! flips a boolean to its opposite.

js
const age = 20;
const hasTicket = true;
console.log(age >= 18 && hasTicket); // true, both are true
console.log(age < 13 || hasTicket); // true, one side is enough
console.log(!hasTicket); // false, flipped

There is a related idea called truthy and falsy. In a true-or-false setting, JavaScript treats some non-boolean values as if they were false. The falsy values are false, 0, "" (an empty string), null, undefined, and NaN. Everything else counts as truthy.

js
const name = "";
console.log(!name); // true, an empty string is falsy

The logical operators combine boolean values: && (and) is true only when both operands are true, || (or) is true when at least one is, and ! (not) inverts a single boolean.

js
const loggedIn = true;
const isAdmin = false;
console.log(loggedIn && isAdmin); // false, both must be true
console.log(loggedIn || isAdmin); // true, one is enough
console.log(!isAdmin); // true

The operators do not stop at real booleans, because JavaScript sorts every value into truthy or falsy. The falsy set is short and worth memorising: false, 0, "", null, undefined, and NaN. Everything else is truthy, including "0", "false", and any non-empty string. This is what lets you write a check like !username to mean "no username was entered".

js
const username = "";
console.log(!username); // true, "" is falsy
console.log(Boolean("hello")); // true, a non-empty string is truthy

The logical operators are &&, ||, and !, and the key insight is that && and || do not return a boolean. They short-circuit: each evaluates the left operand, and only evaluates the right if it has to, then returns one of the actual operands, not a coerced true or false.

a || b returns a if a is truthy, otherwise b. a && b returns a if a is falsy, otherwise b. That behaviour underpins the falsy set: false, 0, "", null, undefined, and NaN are the six falsy values, and everything else is truthy.

js
const name = "";
const display = name || "Guest"; // "Guest", because "" is falsy
const label = "Sofia" && "active"; // "active", left is truthy so it returns the right
console.log(display); // Guest

The || default trick has a sharp edge, which is why ?? (nullish coalescing) exists. || falls back whenever the left side is any falsy value, so 0 || 10 is 10 and "" || "n/a" is "n/a", even when 0 and "" were valid inputs you wanted to keep. ?? falls back only when the left side is null or undefined, treating 0 and "" as real values.

js
const count = 0;
console.log(count || 5); // 5, but 0 was a real value
console.log(count ?? 5); // 0, ?? only replaces null or undefined

When you need to choose between two values based on a condition, a full comparison plus a ternary (condition ? whenTrue : whenFalse) is the one-line form, for example age >= 18 ? "adult" : "minor". That is a preview; the conditionals chapter covers it and the if statement in full.

JunoLogical operators and truthiness&& is "both", || is "either", and ! flips a boolean. On top of that, JavaScript treats some values as false-ish: false, 0, "", null, undefined, and NaN are the falsy ones, and everything else is truthy. That is what lets !name stand for "no name was given".
JunoLogical operators and truthiness&& needs both sides true, || needs one, and ! inverts. Every value is also either truthy or falsy, and the falsy set is small enough to memorise: false, 0, "", null, undefined, NaN. Watch that "0" and "false" are strings, so both are truthy.
JunoLogical operators and truthiness&& and || short-circuit and return an operand, not a boolean, which is why name || "Guest" works as a default. That default has an edge: || replaces every falsy value, so use ?? when 0 and "" are inputs you want to keep. The six falsy values are the whole story, so learn the set and the rest falls out.

Where operators go from here

Operators are the verbs of the language: they take the values you store and turn them into results and decisions. Arithmetic produces new numbers, comparisons produce booleans, and logical operators combine those booleans into the yes-or-no answers your program acts on.

Those booleans do not do much on their own. The next step is using them to steer your code, running one branch when a check passes and another when it fails, which is what the conditionals chapter is about. If any of the values here felt fuzzy, the data types chapter is where numbers, strings, and booleans get their full treatment.