Module 1 · Foundations & First Steps ⏱ 22 min

Conditionals & Loops

By the end of this lesson you will be able to:
  • Branch your code with if / else if / else and compare values safely
  • Combine conditions with &&, ||, and !
  • Repeat work with for, while, and for...of loops
  • Avoid the off-by-one bug and the == versus === trap

Programs that run top to bottom exactly once are rare. Usually you need to make a decision — is the user logged in? is the cart empty? — or repeat an action — process every item, retry until it works. Conditionals and loops are the two control structures that make code useful in the real world.

A conditional runs one block of code or another depending on whether a test is true. The simplest form is if, but real code almost always needs else if chains and else fallbacks. A loop repeats a block while a condition stays true, or while there are more items to visit.

Mastering these two structures means moving from scripts that calculate one thing to programs that handle any input, any size, and any edge case.

flowchart TD
  S["score = 85"] --> Q{"score >= 90?"}
  Q -- "Yes" --> A["Grade: A"]
  Q -- "No" --> Q2{"score >= 80?"}
  Q2 -- "Yes" --> B["Grade: B"]
  Q2 -- "No" --> Q3{"score >= 70?"}
  Q3 -- "Yes" --> C["Grade: C"]
  Q3 -- "No" --> F["Grade: F"]
  style A fill:#e0900b,color:#fff
  style B fill:#e0900b,color:#fff
if / else if / else checks each condition in order and runs the first match.

Comparisons: === versus ==

JavaScript has two equality operators, and they do not mean the same thing. === checks both value and type; == tries to convert types first, which leads to famously surprising results:

0 == ""          // true — string becomes 0
0 === ""         // false — number !== string
null == undefined  // true
null === undefined // false

The rule is simple: always use === and !== unless you have a specific reason not to. Code that relies on == is harder to read and easier to break when data changes. The same discipline applies to comparisons: >, <, >=, and <= coerce as well, so make sure both sides are the expected type.

One subtle case: NaN === NaN is false. If you need to test for NaN, use Number.isNaN(value).

=== is strict; == coerces types and surprises beginners.
console.log(0 == "");
console.log(0 === "");
console.log(5 > "3");
console.log(5 > "10");

Logical operators and truthiness

Combine checks with && (both must be true), || (either is true), and ! (not). These operators short-circuit: && stops and returns the first falsy value it meets; || stops and returns the first truthy value. This is not just an optimisation — it is an idiom:

const name = input || "Anonymous";  // default if input is falsy

Conditions also read truthiness. 0, "", null, undefined, NaN, and false are falsy; everything else is truthy. That means if (count) is shorthand for if (count !== 0 && count !== null && count !== undefined), but it is also a common source of bugs when 0 is a valid value you did not mean to exclude.

A defensive habit: when 0 or "" are legitimate values, be explicit. Write if (count !== 0) rather than if (count). The extra characters prevent a whole class of silent errors.

flowchart TD
  T["Truthy examples"] --> T1["'hello'"]
  T --> T2["42"]
  T --> T3["[]"]
  T --> T4["{}"]
  F["Falsy values"] --> F1["0"]
  F --> F2["''"]
  F --> F3["null"]
  F --> F4["undefined"]
  F --> F5["NaN"]
  F --> F6["false"]
  style T fill:#3776ab,color:#fff
  style F fill:#b45309,color:#fff
Common falsy values in JavaScript; everything else is truthy.

Loops: repeating work

Loops run a block of code multiple times. The classic for loop gives you precise control over a counter:

for (let i = 0; i < 3; i++) {
  console.log(i);
}

for...of is the friendliest way to walk through an array — you get each element directly, no index juggling. Use while when you do not know how many iterations you need in advance, only the condition that should stop them:

let attempts = 3;
while (attempts > 0) {
  attempts--;
}

All three share the same risk: an infinite loop if the exit condition never becomes false. With for the increment is visible in the header, which makes the off-by-one easier to spot. With while the update is somewhere in the body, so it is easier to forget.

Three loop styles for different situations.
const cart = [12, 7, 30];
let total = 0;
for (const price of cart) {
  total += price;
}
console.log(`Total: ${total}`);

for (let i = 1; i <= 3; i++) {
  console.log(`Item ${i}`);
}

let n = 3;
while (n > 0) {
  console.log(n);
  n--;
}
flowchart LR
  INIT["let i = 0"] --> TEST{"i < 3?"}
  TEST -->|"Yes"| BODY["console.log(i)"]
  BODY --> INC["i++"]
  INC --> TEST
  TEST -->|"No"| END["done"]
  style BODY fill:#e0900b,color:#fff
  style END fill:#e0900b,color:#fff
A for loop: initialise, test, run body, increment, repeat until the test fails.
A while loop that exits when a running total reaches a target.
let sum = 0;
let i = 1;
while (sum < 10) {
  sum += i;
  console.log(`added ${i}, sum is ${sum}`);
  i++;
}
console.log(`finished at i=${i}`);
Exercise

Write gradeLabel(score) that returns "A" for 90+, "B" for 80+, "C" for 70+, and "F" otherwise.

function gradeLabel(score) {
  // return "A", "B", "C", or "F"
}
Exercise

What does this print? It loops the array and counts how many values are greater than 4.

const nums = [5, 2, 8, 1, 9];
let count = 0;
for (const n of nums) {
  if (n > 4) {
    count++;
  }
}
console.log(count);
Exercise

Write fizzBuzz(n) that returns an array of strings from 1 to n. For multiples of 3 use "Fizz", multiples of 5 use "Buzz", multiples of both use "FizzBuzz", otherwise use the number as a string. So fizzBuzz(5) returns ["1","2","Fizz","4","Buzz"].

function fizzBuzz(n) {
  // return the array
}
Exercise

This loop is supposed to print 1, 2, 3 and then stop, but it runs forever. Find and fix the bug.

let i = 1;
while (i <= 3) {
  console.log(i);
}
Exercise

Write sumUntilTarget(nums, target) that adds numbers from nums in order until the running total is at least target, then returns that total. If nums runs out before reaching target, return the total of all numbers. Use a for...of loop and break.

function sumUntilTarget(nums, target) {
  // return the total once it reaches target
}

Recap

  • if / else if / else runs the first branch whose condition is true.
  • === and !== are strict; avoid == unless you truly need coercion.
  • NaN === NaN is false; use Number.isNaN() to test for it.
  • &&, ||, and ! combine conditions and short-circuit.
  • for...of is the cleanest way to iterate array elements.
  • while repeats while a condition holds — make sure something inside changes it.
  • Off-by-one bugs come from < versus <=; array indexes stop at length - 1.
  • Be explicit when 0 or "" are valid values; truthiness is a bug magnet.

Next you'll meet arrays: ordered lists of values and the methods that make them powerful.

Checkpoint quiz

What does this print?

let x = 10;
if (x > 5 && x < 20) console.log("yes");
else console.log("no");

How many times does for (let i = 0; i < 3; i++) run its body?

Go deeper — technical resources