JavaScript Basics #3 Control Flow

6 min read

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.

basic if
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.

truthy-check idiom
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 #

ternary
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.

while
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.”

do...while
let answer;
do {
  answer = prompt('enter a number');
} while (isNaN(Number(answer)));

for — classic loop #

Three slots — init ; condition ; post.

classic for
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 #

for...of — modern standard
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() #

value and index together
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.

for...in (on an object)
const user = { id: 'u1', name: 'Curtis', age: 30 };

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}
// id: u1, name: Curtis, age: 30
using for...in on an array — accident
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.

object iteration — recommended pattern
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.

forEach / map / filter preview
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.

break — exit the loop
for (let i = 0; i < 10; i = i + 1) {
  if (i === 5) break;
  console.log(i);
}
// 0, 1, 2, 3, 4
continue — go to next iteration
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 #

basic switch
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).

forgotten break — accident
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.

strict comparison
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.

short if — risky
if (condition)
  doSomething();

The trouble is that once it grows past one line, accidents happen.

missing braces lead to accidents
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/else is 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...in iterates object keys — don’t use on arrays
  • break/continue work in plain loops only — not in method callbacks
  • switch watch 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.

X