The VikiD Truth

Knowledge base / Computing / JavaScript

Javascript | Javascript kata |Node JS | Data Types | Variables | Control Flow | Scope and Environment | Objects | Functions | Events | Prototypal Inheritance | Three.js | 3DE Code Editor |

Javascript Control Flow

Here is the truth about control flow in JavaScript: the keywords are borrowed from C, but the decisions are made by JavaScript’s own rules about truth and equality. if, for and switch will look familiar. What they consider true will not, until you learn the short list below.

What it is, in plain words

Control flow is how a program decides what to run next. JavaScript gives you:

Every branch and loop condition is converted to a boolean using truthiness. Exactly eight values are falsy: false, 0, -0, 0n, "", null, undefined and NaN. Everything else, including [], {}, "0", "false" and Infinity, is truthy.

Why it matters

Most JavaScript bugs in conditions come from two places: loose equality (==) doing surprising type conversions, and truthiness treating 0 or "" as “missing”. Knowing the rules lets you write conditions that mean what you think, and read other people’s code without second-guessing.

An everyday analogy: a lenient doorman and a strict one

== is a lenient doorman. Show him the number 5 or the text “5” and he lets you both in, because he converts you to the same thing first. Sometimes he converts in very odd ways (0 == "" is true). === is the strict doorman: same type and same value, or you are not coming in. Use the strict one unless you have a reason.

A hands-on example

Run this with node flow.js (or paste into the browser console):

// flow.js
const items = [
  { name: "pil",   stars: 5 },
  { name: "emacs", stars: 0 },
  { name: "vim" },               // stars missing
];

for (const { name, stars } of items) {
  // ?? only falls back for null/undefined, so 0 stays 0
  const s = stars ?? "unrated";
  // || would wrongly turn 0 into "unrated"
  console.log(name.padEnd(6), s, stars || "unrated");
}

// switch uses === to compare
function kind(x) {
  switch (typeof x) {
    case "number": return Number.isNaN(x) ? "NaN" : "number";
    case "string": return "string";
    case "object": return x === null ? "null" : Array.isArray(x) ? "array" : "object";
    default:       return typeof x;
  }
}
console.log([1, "a", null, [], {}, NaN, 10n].map(kind));

// Loop control and labels
outer: for (let i = 0; i < 3; i++) {
  for (let j = 0; j < 3; j++) {
    if (j === 1) continue;
    if (i === 2) break outer;
    console.log(i, j);
  }
}

// Asynchronous control flow
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
async function main() {
  try {
    await wait(100);
    throw new Error("demo");
  } catch (e) {
    console.log("caught", e.message);
  } finally {
    console.log("always runs");
  }
}
main();

Notice stars ?? "unrated" versus stars || "unrated" for emacs: the first keeps 0, the second throws it away. The nullish coalescing operator ?? (ES2020) fixed a whole category of bugs.

Common misconceptions

Connections

Equality

Objects

  Obj1 === obj2   // always false
  

unless they are literally the same object. }

If Statements

  let condition  = true;
  let condition2 = false;
  if (condition) {
  console.log("Do action");
  } else if (condition2) {
  console.log("take alternative actions");
  } else {
  console.log("take default action");
  }

Last edited · 11 revisions

Tags: #Computing