Home Module 09 Operators

1. Introduction

Operators are the symbols that act on values. You already know + from arithmetic — that is an operator. JavaScript has many more: operators for comparing values (===, >), combining booleans (&&, ||), assigning values (=, +=), and making quick decisions (?:).

Understanding operators is essential before you can write conditions (Lesson 5) and loops (Lesson 6), because those features are built entirely from operator expressions.

This lesson covers:

  • Arithmetic operators
  • Assignment operators
  • Comparison operators
  • Logical operators
  • The ternary (conditional) operator
  • The nullish coalescing and optional chaining operators

2. Theory

2.1 Arithmetic operators

OperatorNameExampleResult
+Addition5 + 38
-Subtraction5 - 32
*Multiplication5 * 315
/Division10 / 42.5
%Remainder (modulo)10 % 31
**Exponentiation2 ** 8256
++Incrementx++ or ++xadds 1
--Decrementx-- or --xsubtracts 1
let x = 10;
x++;          // x is now 11
x--;          // x is now 10 again
console.log(x % 2 === 0); // true — x is even

// + also concatenates strings
console.log('Hello' + ' ' + 'World'); // 'Hello World'

// Watch out for mixed types
console.log('5' + 3);  // '53' (string concat)
console.log('5' - 3);  // 2   (numeric subtraction)

2.2 Assignment operators

OperatorEquivalent toExample
=assignx = 5
+=x = x + nx += 3
-=x = x - nx -= 2
*=x = x * nx *= 4
/=x = x / nx /= 2
%=x = x % nx %= 3
**=x = x ** nx **= 2
let score = 0;
score += 10;  // score = 10
score += 5;   // score = 15
score -= 3;   // score = 12
score *= 2;   // score = 24
console.log(score); // 24

2.3 Comparison operators

Comparison operators always return a boolean (true or false).

OperatorNameExampleResult
===Strict equal5 === 5true
!==Strict not equal5 !== 3true
>Greater than5 > 3true
<Less than5 < 3false
>=Greater or equal5 >= 5true
<=Less or equal3 <= 5true
==Loose equal (avoid)'5' == 5true
!=Loose not equal (avoid)'5' != 5false
const age = 18;
console.log(age >= 18);  // true
console.log(age === 21); // false
console.log(age !== 21); // true

const name = 'Alice';
console.log(name === 'Alice'); // true
console.log(name === 'alice'); // false — case-sensitive

2.4 Logical operators

OperatorNameReturns true when
&&ANDBoth sides are truthy
||ORAt least one side is truthy
!NOTThe operand is falsy
const isLoggedIn  = true;
const isPremium   = false;
const hasDiscount = true;

// AND — both must be true
console.log(isLoggedIn && isPremium);    // false
console.log(isLoggedIn && hasDiscount);  // true

// OR — at least one must be true
console.log(isPremium || hasDiscount);   // true
console.log(isPremium || !isLoggedIn);   // false

// NOT — flips the boolean
console.log(!isLoggedIn); // false
console.log(!isPremium);  // true

Short-circuit evaluation

Logical operators do not always evaluate both sides. They "short-circuit" when the result is already known:

// && short-circuits on the first falsy value
false && console.log('never printed'); // console.log never runs

// || short-circuits on the first truthy value
true || console.log('never printed');  // console.log never runs

// || as a default value (classic pattern)
const username = '' || 'Guest';
console.log(username); // 'Guest' — because '' is falsy

// && as a conditional execution
const user = { name: 'Alice' };
user && console.log(user.name); // 'Alice' — runs because user is truthy

2.5 Ternary operator (conditional expression)

The ternary operator is a compact one-line if/else:

// condition ? valueIfTrue : valueIfFalse

const age = 20;
const status = age >= 18 ? 'adult' : 'minor';
console.log(status); // 'adult'

// Equivalent if/else
let status2;
if (age >= 18) {
  status2 = 'adult';
} else {
  status2 = 'minor';
}

// In a template literal
const price = 10;
const label = `Price: $${price} ${price > 5 ? '(expensive)' : '(cheap)'}`;
console.log(label); // 'Price: $10 (expensive)'

Use ternaries for simple yes/no value decisions. For complex logic, use a full if/else for readability.

2.6 Nullish coalescing operator (??)

?? returns the right side only if the left side is null or undefined (unlike || which reacts to any falsy value).

// || — triggers on ANY falsy value (0, '', false, null, undefined)
const count = 0 || 10;  // 10 — but 0 might be a valid value!

// ?? — triggers ONLY on null or undefined
const count2 = 0 ?? 10;  // 0 — correct, 0 is a valid value
const count3 = null ?? 10;  // 10
const count4 = undefined ?? 10; // 10

// Practical use: config with defaults
const config = {
  timeout: 0,         // valid value
  retries: null,      // not set
};
const timeout = config.timeout ?? 3000; // 0 (keeps the 0)
const retries = config.retries ?? 3;    // 3 (fills in the null)

2.7 Optional chaining operator (?.)

?. safely accesses a property or method — if the object is null or undefined, it returns undefined instead of throwing an error.

const user = null;

// Without optional chaining — TypeError!
console.log(user.name); // TypeError: Cannot read properties of null

// With optional chaining — safe
console.log(user?.name);       // undefined
console.log(user?.address?.city); // undefined (chained)

// On methods
const arr = null;
console.log(arr?.map(x => x * 2)); // undefined

// Combined with ??
const city = user?.address?.city ?? 'Unknown';
console.log(city); // 'Unknown'

2.8 Operator precedence

Operators follow a precedence order (like BODMAS in math). Use parentheses to make your intent explicit:

// Without parentheses — might not do what you expect
console.log(2 + 3 * 4);   // 14 (multiplication first)

// With parentheses — explicit
console.log((2 + 3) * 4); // 20

// Logical precedence: ! then && then ||
console.log(true || false && false); // true (&&  before ||)
console.log((true || false) && false); // false (parentheses override)

3. Real World Example

// E-commerce discount calculation
function calculatePrice(basePrice, userType, couponCode) {
  let discount = 0;

  // Logical operators for combined conditions
  if (userType === 'premium' && basePrice > 50) {
    discount += 0.10; // 10% for premium users on items over $50
  }

  if (couponCode === 'SAVE20' || couponCode === 'WELCOME') {
    discount += 0.20; // 20% coupon
  }

  // Cap total discount at 30%
  discount = Math.min(discount, 0.30);

  const finalPrice = basePrice * (1 - discount);

  // Ternary for free shipping message
  const shipping = finalPrice >= 50 ? 'FREE' : '$5.99';

  return {
    original: basePrice,
    discount: (discount * 100).toFixed(0) + '%',
    final: finalPrice.toFixed(2),
    shipping,
  };
}

console.log(calculatePrice(80, 'premium', 'SAVE20'));
// { original: 80, discount: '30%', final: '56.00', shipping: 'FREE' }

4. Code Example

<script>
  // ---- Arithmetic ----
  let x = 10;
  console.log(x + 3);  // 13
  console.log(x % 3);  // 1 (remainder)
  x += 5;              // x = 15
  x++;                 // x = 16
  console.log(x);      // 16

  // ---- Comparison ----
  const age = 25;
  console.log(age >= 18);      // true
  console.log(age === '25');   // false — different types
  console.log(age === 25);     // true

  // ---- Logical ----
  const loggedIn = true;
  const verified = false;
  console.log(loggedIn && verified);  // false
  console.log(loggedIn || verified);  // true
  console.log(!verified);             // true

  // ---- Ternary ----
  const grade = 85;
  const result = grade >= 60 ? 'Pass' : 'Fail';
  console.log(result); // 'Pass'

  const label = `Grade: ${grade} — ${grade >= 90 ? 'A' : grade >= 80 ? 'B' : grade >= 70 ? 'C' : 'D/F'}`;
  console.log(label); // Grade: 85 — B

  // ---- Nullish Coalescing ----
  const userInput = '';         // empty string from form
  const display1 = userInput || 'Anonymous';  // 'Anonymous' (|| reacts to empty)
  const display2 = userInput ?? 'Anonymous';  // '' (only reacts to null/undefined)
  console.log(display1, display2);

  // ---- Optional Chaining ----
  const user = { profile: { city: 'London' } };
  const noProfile = null;
  console.log(user?.profile?.city);       // 'London'
  console.log(noProfile?.profile?.city);  // undefined (no error)
  console.log(noProfile?.profile?.city ?? 'Unknown'); // 'Unknown'
</script>

5. Code Breakdown

x += 5 and x++

Both are shorthand assignment operators. x += 5 means "add 5 to x and store the result." x++ is post-increment — it adds 1. These are so common they are worth memorising immediately.

age === '25' — false

The number 25 and the string '25' are different types. Strict equality (===) requires both value and type to match. This is why form inputs (which always return strings) must be converted before numeric comparison.

Chained ternary for grade label

You can chain ternaries but keep it readable — no more than 2–3 levels. Deeper nesting should use if/else or a lookup table instead.

|| vs ?? on empty string

'' is falsy, so || 'Anonymous' replaces it with 'Anonymous'. ?? only fires on null/undefined, so it keeps the empty string. If an empty string is a valid value (e.g., a user who chose not to provide their name), use ??.

Optional chaining chain

noProfile?.profile?.city — the first ?. checks if noProfile is null/undefined. It is, so the whole expression returns undefined without attempting to access .profile (which would throw). The ?? then substitutes 'Unknown'.

6. Common Mistakes

Mistake 1 — Using = instead of === in comparisons

let x = 5;
if (x = 10) { // BUG: assigns 10 to x, then 10 is truthy
  console.log('always runs');
}
// Correct:
if (x === 10) { ... }

Mistake 2 — Using || for defaults when 0 or '' are valid values

const qty = 0; // user typed 0
const display = qty || 1; // Wrong — shows 1 when user meant 0

const display2 = qty ?? 1; // Correct — shows 0

Mistake 3 — Confusing && and ||

// "User must be logged in OR an admin" — wrong:
if (isLoggedIn && isAdmin) { ... }  // requires BOTH

// Correct:
if (isLoggedIn || isAdmin) { ... }  // at least one

Mistake 4 — Forgetting operator precedence

// Looks like: (a === 1 || a === 2) but is actually: a === (1 || 2)
if (a === 1 || 2) { ... } // BUG — always true (2 is truthy)

// Correct:
if (a === 1 || a === 2) { ... }

Mistake 5 — Deeply nested ternaries

// Hard to read
const x = a ? b ? 'x' : 'y' : c ? 'z' : 'w';

// Better — use if/else or a lookup
let x;
if (a && b)  x = 'x';
else if (a)  x = 'y';
else if (c)  x = 'z';
else         x = 'w';

7. Best Practices

  1. Always use === and !== — never == or !=.
  2. Use ?? instead of || when the left value could be a valid falsy like 0 or ''.
  3. Use ?. for safe property access on values that might be null/undefined.
  4. Use += / -= shortcuts for readability when updating variables.
  5. Use ternary only for simple two-option expressions — use if/else for multi-branch logic.
  6. Add parentheses when mixing && and || — do not rely on precedence rules being remembered by the next reader.
  7. Avoid incrementing with ++/-- in the middle of expressions — use them on their own line to avoid confusing post vs pre-increment behaviour.

8. Practice Exercise

  1. Declare let a = 15. Use each assignment operator (+=, -=, *=, /=, %=) once and log the result after each.
  2. Write three comparison expressions that return true and three that return false. Log each.
  3. Write a function canDrive(age, hasLicence) that returns true only if both conditions are met. Test with various inputs.
  4. Write a function gradeLabel(score) using ternary operators that returns 'A' (90+), 'B' (80–89), 'C' (70–79), or 'F' (<70).
  5. Given const config = { port: 0, host: null }, use ?? to set defaults: port 3000, host 'localhost'.

Bonus

  • Write a safeGet(obj, path) function that uses optional chaining to safely access a nested property path (e.g., safeGet(user, 'address.city')).

9. Assignment

Build a "Loan Eligibility Checker."

  1. Declare variables: income, creditScore, hasDebts, loanAmount.
  2. Write a function checkEligibility(income, creditScore, hasDebts, loanAmount) that:
    • Returns 'Approved' if: credit score >= 700 AND income >= 3× the loan amount AND NOT has debts.
    • Returns 'Conditionally Approved' if: credit score >= 600 AND income >= 4× the loan amount.
    • Returns 'Declined' otherwise.
  3. Test with at least 5 different inputs covering all three outcomes.
  4. Display the result in an HTML paragraph using a ternary to add colour styling (green for Approved, orange for Conditionally, red for Declined).

Deliverable: One HTML file with embedded JS. Log each test case result to the console.

10. Interview Questions

  1. What is the difference between == and ===?
    == performs type coercion before comparing (e.g., '5' == 5 is true). === compares value and type strictly without coercion. Always use ===.
  2. What does the && operator return?
    It returns the first falsy value, or the last value if all are truthy. E.g., null && 'hello' returns null; 1 && 'hello' returns 'hello'. This is why it is used for short-circuit patterns like user && user.name.
  3. What is the difference between || and ???
    || uses the right side for any falsy left value (0, '', false, null, undefined). ?? uses the right side only for null and undefined. Use ?? when 0 or empty string are valid values.
  4. What does optional chaining (?.) do?
    It safely accesses a property or calls a method — if the left side is null or undefined, it returns undefined instead of throwing a TypeError.
  5. What is the ternary operator?
    condition ? valueIfTrue : valueIfFalse — a compact expression that evaluates to one of two values based on a condition. Useful for inline assignments and template literals.
  6. What does the % operator do?
    It returns the remainder after integer division. 10 % 3 = 1. Common use cases: checking if a number is even (n % 2 === 0), cycling through values, and working with time.

11. Additional Resources

  • MDN — Expressions and operators — complete operator reference
  • javascript.info — Operators — beginner-friendly explanations
  • javascript.info — Nullish coalescing ??
  • javascript.info — Optional chaining ?.
  • MDN — Operator precedence table