JavaScript Basics #3 Control Flow
In #2 Variables and Types you saw the tools for handling values. This post is about branching and looping with those values.
if / else if / else
#
The most familiar form of branching.
const score = 85;
if (score >= 90) {
console.log('A');
} else if (score >= 80) {
console.log('B');
} else if (score >= 70) {
console.log('C');
} else {
console.log('F');
}The condition becomes a truthy/falsy check. If it’s one of the 7 falsy values you saw in #2 (false, 0, -0, 0n, ‘’, null, undefined, NaN), it goes to else.
const name = 'Curtis';
if (name) {
// when name is a non-empty string
console.log(`hi, ${name}`);
}
// nearly identical — explicit
if (name !== '') {
console.log(`hi, ${name}`);
}For brevity and clear intent, use a truthy check; when you want to be clear about what’s empty, use an explicit comparison. Both are common.
Ternary operator — fits short branches #
const score = 85;
const grade = score >= 60 ? 'pass' : 'fail';
// condition ? when true : when false
In value-deciding cases (variable assignment, JSX, object fields) it’s shorter and tidier than if/else. Once you nest two levels it gets hard to read quickly — at that point if/else if is better.
while and do...while
#
Loop while the condition is true.
let count = 0;
while (count < 5) {
console.log(count);
count = count + 1;
}
// 0, 1, 2, 3, 4
do...while — a variant that runs at least once unconditionally. Fits places like user input where you “take it then check the condition.”
let answer;
do {
answer = prompt('enter a number');
} while (isNaN(Number(answer)));for — classic loop
#
Three slots — init ; condition ; post.
for (let i = 0; i < 5; i = i + 1) {
console.log(i);
}
// 0, 1, 2, 3, 4
Same shape as C/Java. When you must touch the index directly (reverse, skipping, walking two arrays in lockstep), this is still the clearest. For ordinary array iteration, for...of is usually nicer to read.
for...of — the standard for array iteration
#
const fruits = ['apple', 'banana', 'grape'];
for (const fruit of fruits) {
console.log(fruit);
}
// apple, banana, grape
Pulls values from an array one at a time. When you don’t need the index, this is almost always cleaner.
Index too — entries()
#
const fruits = ['apple', 'banana', 'grape'];
for (const [i, fruit] of fruits.entries()) {
console.log(`${i}: ${fruit}`);
}
// 0: apple, 1: banana, 2: grape
fruits.entries() is an iterator producing [index, value] pairs. Destructuring is covered in detail in #5 Objects and Arrays — for now think of it as “syntax for receiving two variables at once.”
for...in — a rarely-used variant
#
The name is similar and confusing — but it iterates object keys. Using it on an array is a recipe for trouble.
const user = { id: 'u1', name: 'Curtis', age: 30 };
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}
// id: u1, name: Curtis, age: 30
const arr = ['a', 'b', 'c'];
arr.foo = 'oops';
for (const key in arr) {
console.log(key);
}
// 0, 1, 2, foo ← foo shows up too
The array’s indices (0, 1, 2) along with the property we added (foo) all show up. So never iterate arrays with for...in. For objects, Object.keys/values/entries is safer.
const user = { id: 'u1', name: 'Curtis', age: 30 };
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}Array method iteration — preview of forEach, map, filter
#
Beyond for...of, arrays have functional-style iteration methods.
const numbers = [1, 2, 3, 4, 5];
// side effects only — forEach
numbers.forEach((n) => console.log(n));
// transform — map
const doubled = numbers.map((n) => n * 2);
// [2, 4, 6, 8, 10]
// filter — filter
const evens = numbers.filter((n) => n % 2 === 0);
// [2, 4]
These methods are covered in earnest in #5 Objects and Arrays. For now, just remember “there are options beyond for loops.”
break and continue
#
Two keywords for flow control inside loops.
for (let i = 0; i < 10; i = i + 1) {
if (i === 5) break;
console.log(i);
}
// 0, 1, 2, 3, 4
for (let i = 0; i < 5; i = i + 1) {
if (i === 2) continue;
console.log(i);
}
// 0, 1, 3, 4 (2 skipped)
Inside methods like forEach/map/filter, break/continue don’t work. To stop early, switch to for...of, or use methods like some/every (in the next series).
switch — branch on multiple values
#
const day = 'mon';
switch (day) {
case 'mon':
case 'tue':
case 'wed':
case 'thu':
case 'fri':
console.log('weekday');
break;
case 'sat':
case 'sun':
console.log('weekend');
break;
default:
console.log('unknown');
}Cases are checked top to bottom and execute from the matching one until a break. This is the common gotcha — forget break and the next case also runs (fallthrough).
switch (day) {
case 'mon':
console.log('Monday!');
// missing break
case 'tue':
console.log('Tuesday!');
break;
}
// when day is 'mon' — both Monday! and Tuesday! print
switch comparison is strict (===)
#
switch matching always uses ===. No coercion happens.
switch (5) {
case '5': console.log('a'); break; // doesn't match
case 5: console.log('b'); break; // matches here
}This is a good thing — you avoid ==’s pitfalls.
Short-if pitfall — omitting braces #
JavaScript lets you omit braces in a short if.
if (condition)
doSomething();The trouble is that once it grows past one line, accidents happen.
if (condition)
doSomething();
alsoThis(); // always runs (no relation to if)
The indentation looks like it’s inside the if, but it always runs. Always using braces, even for short ifs, is a safer convention. ESLint’s curly rule enforces exactly this.
Wrap-up #
What we covered:
if/elseis a truthy/falsy check or an explicit comparison- Short branches with the ternary; longer ones with
if/else if - Standard array iteration is
for...of for...initerates object keys — don’t use on arraysbreak/continuework in plain loops only — not in method callbacksswitchwatch for fallthrough; comparison is always===- Always use braces, even for short ifs
In the next post (#4 Functions) we cover JavaScript’s functions — the three definition styles (declaration/expression/arrow), parameter patterns, and what hoisting is.