Operators

Symbols that perform actions on values, like math or comparisons.

What is it?

You constantly need to do things with values — add two numbers together, check which of two is bigger, or combine two conditions into one decision. JavaScript gives you a set of symbols, called operators, that do exactly that: each one takes one or more values and produces a result.

A few groups you'll use daily:

  • Arithmetic: + - * / % for math
  • Comparison: === !== > < >= <= for comparing two values
  • Logical: && || ! for combining true/false conditions
  • Assignment: = += -= for storing or updating a value

Explain like I'm 10

Operators are like the buttons on a calculator — each one takes the numbers you've entered and does a specific, predictable action with them.

Examples

Arithmetic and comparison

const total = 10 + 5;      // 15
const isEqual = 10 === 10; // true
const isBigger = 10 > 20;  // false

Logical operators combining conditions

const age = 20;
const hasTicket = true;

const canEnter = age >= 18 && hasTicket;
console.log(canEnter); // true

const skipLine = age < 12 || age > 65;
console.log(skipLine); // false

&& requires both sides to be true; || only needs one side to be true. Both are common ways to combine multiple conditions into one decision.

How it works

Each operator takes one or two values (called operands) and produces a result. + takes two numbers and produces their sum. === takes two values and produces a boolean — true if they match exactly, false otherwise.

Why does it exist?

Programs constantly need to calculate, compare, and combine values to make decisions. Operators are the basic building blocks for all of that logic.

When to use it

Use arithmetic operators whenever your code needs to calculate something, and comparison operators whenever it needs to make a decision. Logical operators (&&, ||) come in whenever that decision depends on more than one condition at once.

When not to use it

Avoid == in new code — its type-converting comparisons cause more bugs than they prevent; === should be your default. And don't chain more logical operators into one expression than a reader can parse at a glance — split a complex condition into a well-named variable instead.

Common mistakes

  • Using = (assignment) when you meant === (comparison).

  • Using == instead of === — == converts types before comparing, which can cause surprising results like "5" == 5 being true.

  • Forgetting operator precedence, e.g. assuming 2 + 3 * 4 equals 20 instead of 14.

Practice exercises

  1. Easy:

    Write an expression that adds two numbers and multiplies the result by 2.

  2. Medium:

    Use === to compare two values of different types and explain the result.

  3. Hard:

    Write a one-line expression using && and || that checks if a user is either an admin or (a member and over 18).

Interview questions

What's the difference between `==` and `===`?

== (loose equality) converts operands to a common type before comparing if they differ, which can produce surprising matches like "5" == 5. === (strict equality) compares both value and type with no conversion, so mismatched types are never equal — it's the safer default in almost every case.

What does the `%` (modulo) operator do?

It returns the remainder left over after dividing the left operand by the right one — 10 % 3 is 1, because 3 goes into 10 three times with 1 left over. It's the standard way to check divisibility (n % 2 === 0 for even) or wrap a number into a fixed range.

What's the difference between `=`, `==`, and `===`?

= is assignment — it stores a value into a variable and isn't a comparison at all. == and === are both comparisons that produce a boolean; == allows type conversion before comparing, === doesn't. Accidentally writing = inside a condition where ==/=== was intended is a classic bug, since if (x = 5) assigns 5 to x and then evaluates the condition as truthy.

What do `&&` and `||` do?

&& (logical AND) requires both operands to be truthy for the overall expression to be truthy; || (logical OR) only needs at least one operand to be truthy. Both are used to combine multiple conditions into a single true/false decision.

What does the `!` operator do?

It's logical NOT — it converts its operand to a boolean and flips it, so !true is false and !0 is true (since 0 is falsy). Doubling it, !!value, is a common shorthand for converting any value to its boolean truthy/falsy equivalent.

What is short-circuit evaluation?

JavaScript stops evaluating a logical expression as soon as the overall result is already determined by the operands it's seen so far, instead of always evaluating every operand — in a || b, if a is truthy the result is already known to be truthy, so b is never evaluated at all.

Why does `a || b` sometimes not evaluate `b` at all?

Because || only needs one truthy operand to know the whole expression is truthy — once a turns out truthy, evaluating b couldn't change the answer, so the engine skips it entirely. This matters if b is a function call with side effects: those side effects simply won't happen.

If both operands of `&&` are truthy, what does the expression actually evaluate to?

Not true — && returns the second operand's actual value if the first is truthy (since the first no longer determines the result), so "a" && "b" evaluates to "b", not true. It only looks like a boolean when the operands themselves happen to be booleans.

If the first operand of `||` is truthy, what does the expression evaluate to?

The first operand's own value, not true — || returns as soon as it finds a truthy operand, so "hello" || "fallback" evaluates to "hello". This is exactly the mechanism default-value patterns like const name = input || "Guest"; rely on.

What is operator precedence, and how does it explain the result of `2 + 3 * 4`?

Precedence is the fixed order in which JavaScript evaluates operators when an expression mixes several of them, independent of left-to-right reading order. Multiplication binds tighter than addition, so 3 * 4 is computed first (12), then 2 + 12 gives 14, not 20.

What's the difference between `x += 1` and `x = x + 1`?

Functionally nothing — += is shorthand that reads x, adds 1, and reassigns the result back to x, exactly what the longer form does explicitly. It exists purely for brevity when updating a variable based on its own current value.

What is the nullish coalescing operator (`??`), and how does it differ from `||`?

a ?? b evaluates to b only when a is specifically null or undefined — any other falsy value like 0, "", or false is left alone and returned as-is. || falls back to b for any falsy a, which incorrectly overrides legitimate values like 0 or an empty string that were never meant to trigger a default.

What is optional chaining (`?.`), and what problem does it solve?

It short-circuits to undefined instead of throwing when you try to access a property through a null or undefined value partway down a chain — user?.address?.city returns undefined safely if user or address is missing, instead of throwing TypeError: Cannot read properties of undefined.

What's the difference between `++x` (prefix) and `x++` (postfix)?

Both increment x by 1, but they differ in what the expression itself evaluates to: ++x increments first and evaluates to the new value; x++ evaluates to the old value first, and the increment happens after. This only matters when the increment is used inline as part of a larger expression, e.g. arr[i++] uses the current i for indexing, then increments it.

Why do `&&` and `||` return one of their actual operand values instead of a plain boolean?

It's a deliberate design choice that enables patterns beyond pure boolean logic — default values (input || "default"), guarded property access (user && user.name), and conditionally running a function (isReady && doThing()) all rely on these operators passing through a real value rather than collapsing everything to true/false.

What does `NaN == NaN` evaluate to? What about `NaN === NaN`?

Both are false. Neither loose nor strict equality special-cases NaN — per the IEEE 754 spec its underlying floats follow, NaN is defined to never equal any value, including itself, regardless of which equality operator is used.

Why does `0.1 + 0.2 === 0.3` evaluate to `false`?

JavaScript numbers are stored as IEEE 754 double-precision floats, which can't represent most decimal fractions exactly in binary. 0.1 + 0.2 actually computes to something like 0.30000000000000004, which is not bit-for-bit equal to the literal 0.3 — comparing floating-point numbers for exact equality is unreliable in any language that uses this representation, not just JavaScript.

Output-prediction: what does `console.log(1 < 2 < 3)` log?

true, but not for the reason it looks like. < is left-associative, so it evaluates as (1 < 2) < 3 — 1 < 2 is true, and then true < 3 coerces true to 1, and 1 < 3 is true. It happens to give the mathematically "correct" answer here, but the same pattern with different numbers, e.g. 3 < 2 < 1, gives a misleading true too, since it becomes false < 1 → 0 < 1.

Output-prediction: what do `console.log("5" == 5)` and `console.log("5" === 5)` log?

true then false. == converts the string "5" to the number 5 before comparing, so they match. === refuses to convert types at all, and a string is never strictly equal to a number regardless of its content.

Output-prediction: what do `console.log(null ?? "default")` and `console.log(0 ?? "default")` log?

"default" then 0. ?? only falls back when the left side is null or undefined — null qualifies, but 0 is a perfectly valid, non-nullish value, so it's returned as-is even though it's falsy.

Output-prediction: what does `let x = 5; console.log(x++ + ++x);` log?

It logs 12. x++ evaluates to the current value 5 (then bumps x to 6); ++x then bumps x to 7 first and evaluates to 7. The expression becomes 5 + 7, which is 12, and x ends up 7.