Skip to main content
Adapted, not copied

Original notes for JavaScript360.org — topic order inspired by Eloquent JavaScript (4th ed.) by Marijn Haverbeke. See the full attribution note.

Chapter 2: Program Structure

Estimated time: ~40 minutes · Chapter type: Concept

What you'll learn

  • The difference between an expression and a statement
  • How to declare bindings with let, const, and (why to avoid) var
  • Every control-flow construct you need for everyday code: if, loops, and switch

Expressions vs. statements

An expression is any fragment of code that produces a value: 2 + 2, "hi".toUpperCase(), age > 18. Expressions can nest inside other expressions.

A statement is a full instruction — a complete "step" in your program. A program is a sequence of statements, executed top to bottom. Some statements are just an expression followed by a semicolon (an "expression statement"); others, like if or for, control which other statements run and how many times.

3 + 4;              // an expression statement — computes 7 and throws it away
console.log(3 + 4); // a statement whose expression has a useful side effect

Semicolons in JavaScript are technically optional in many places thanks to "automatic semicolon insertion," but relying on that is a common source of subtle bugs. This course — and most professional style guides — write them explicitly.

Bindings: let, const, and var

A binding (informally, a "variable") gives a name to a value so you can refer to it later.

let score = 0;
score = score + 10; // re-assignable
const MAX_PLAYERS = 4;
// MAX_PLAYERS = 5; // TypeError: Assignment to constant variable.
  • const creates a binding that can't be reassigned. Use it by default — most values in a well-written program never need to change identity.
  • let creates a binding that can be reassigned. Use it when a value genuinely needs to change, like a loop counter or a running total.
  • var is the old way (pre-2015) of declaring bindings. Avoid it in new code — it doesn't respect block scope (see below), which leads to bugs that let/const were specifically designed to prevent.

Important nuance: const prevents reassignment, not mutation. A const array or object can still have its contents changed:

const cart = [];
cart.push('keyboard'); // fine — the array's contents changed, the binding didn't
// cart = ['mouse']; // TypeError — this would be reassignment

The environment

At any point while a program runs, the collection of all bindings currently in scope is called the environment. Every time you declare a new binding, you're adding to that environment; every time a function is called, JavaScript sets up a fresh, temporary environment for its local bindings (much more on this in Chapter 3).

Functions, briefly

You'll get a full chapter on functions next, but you'll need console.log immediately, so here's the shape of a function call:

console.log('Hello, console!'); // prints to the browser or terminal console

console.log is a function — a value that can be called to run a block of code, optionally with input (arguments) and output (a return value). We'll build our own in Chapter 3.

Control flow: if / else

Programs rarely run every line unconditionally — usually you want to run some code only if something is true.

const hour = 14;

if (hour < 12) {
console.log('Good morning');
} else if (hour < 18) {
console.log('Good afternoon');
} else {
console.log('Good evening');
}

For simple two-way choices that produce a value (rather than run a statement), the conditional (ternary) operator is more concise:

const status = age >= 18 ? 'adult' : 'minor';

while and do...while loops

A while loop repeats its body for as long as its condition stays true, checking the condition before each pass:

let count = 3;
while (count > 0) {
console.log(count);
count = count - 1;
}
console.log('Liftoff!');

do...while is the same idea but checks the condition after each pass, guaranteeing the body runs at least once — useful for things like "keep asking the user until they enter valid input":

let input;
do {
input = promptUser(); // pretend function
} while (!isValid(input));

Indenting code

Whitespace has no meaning to the JavaScript engine, but consistent indentation (2 or 4 spaces per nesting level — pick one and stick to it) is what makes code readable by humans, which is arguably the more important audience. Most teams enforce this automatically with a formatter like Prettier rather than debating it by hand.

for loops

Most loops follow the same pattern — set up a counter, check a condition, update the counter — and the for loop bundles all three into one line:

for (let i = 0; i < 5; i++) {
console.log(`Row ${i}`);
}

Read it as: initialize (let i = 0), while this is true (i < 5), do this after each pass (i++). This is exactly equivalent to the while loop above, just more compact and with the counter's scope neatly contained.

Modern JavaScript also gives you for...of, which iterates directly over the values in an array (or any other "iterable" — see Chapter 6):

const fruits = ['apple', 'banana', 'cherry'];
for (const fruit of fruits) {
console.log(fruit);
}

Prefer for...of whenever you just need each value and don't care about the index — it's shorter and eliminates an entire class of off-by-one bugs.

Breaking out of a loop

break exits a loop immediately; continue skips to the next iteration without finishing the current one.

// Find the first number over 100 that's divisible by 7
for (let n = 101; ; n++) {
if (n % 7 !== 0) continue;
console.log(`Found it: ${n}`);
break;
}

(An empty condition in a for loop, as above, means "run forever" — you're expected to break out yourself.)

Updating bindings succinctly

A handful of shorthand operators exist because "take a binding, do something with it, and store the result back" is such a common pattern:

let total = 0;
total += 5; // total = total + 5
total *= 2; // total = total * 2
let i = 0;
i++; // i = i + 1 (post-increment)
++i; // i = i + 1 (pre-increment — the difference matters when used inline)

Dispatching on a value with switch

When you're comparing one value against many possible exact matches, a chain of if/else if gets noisy. switch handles this more clearly:

function describeDay(day) {
switch (day) {
case 'Sat':
case 'Sun':
return 'Weekend';
case 'Mon':
case 'Tue':
case 'Wed':
case 'Thu':
case 'Fri':
return 'Weekday';
default:
return 'Unknown';
}
}

Two easy-to-forget rules: cases fall through to the next case unless you return or break, and stacking case labels with no code between them (as with 'Sat'/'Sun' above) is the standard way to group multiple matches under one block.

Capitalization and naming conventions

JavaScript doesn't enforce a naming style, but the ecosystem has strong conventions worth following so your code reads naturally to other JavaScript developers:

  • camelCase for variables and functions: userAge, calculateTotal.
  • PascalCase for classes and constructor functions: ShoppingCart, HttpClient.
  • UPPER_SNAKE_CASE for true constants that represent fixed configuration: MAX_RETRIES.

Comments

// A single-line comment

/*
A block comment,
spanning multiple lines.
*/

Comments should explain why, not what — well-named bindings and functions should already make the "what" obvious. // increment i by one next to i++ adds noise, not information.

Key takeaways

  • Expressions produce values; statements are the complete instructions that make up a program.
  • Default to const; use let only when a binding truly needs to be reassigned; avoid var in new code.
  • for...of is usually cleaner than a classic counting for loop when you just need each value.
  • switch shines when comparing one value against several exact possibilities; watch out for fall-through.

Try it yourself

  1. Write a for loop that prints every odd number from 1 to 19.
  2. Rewrite that loop using while instead of for.
  3. Write a switch statement that maps a numeric grade (0–100) to a letter grade ('A', 'B', 'C', 'D', 'F') using if/else if first, then think about why switch isn't actually a good fit here (hint: it's built for exact matches, not ranges).
Hints
  1. Start i at 1 and increment by 2 each pass instead of 1.
  2. You'll need to declare the counter before the loop and update it manually inside the body.
  3. switch compares with strict equality against exact values — ranges need if/else if with comparison operators instead.

Summary

Programs are sequences of statements, and the tools in this chapter — bindings, conditionals, and loops — are the load-bearing walls of every program you'll ever write, no matter how sophisticated it gets. Next up: functions, which let you package a sequence of statements into a single, reusable, named unit.

 
Up nextCh. 3Functions~45 min

🌟 Join the JavaScript360 community

Connect with fellow JS developers, and get free interview prep, React challenges, and DSA guides.

Join on WhatsApp →