Operators, Coercion and Numbers

Operators look familiar and then surprise you: == coerces, + prefers strings, and every number is a float. This lesson builds a precise model so the surprises become predictable.

Arithmetic — and the + trap

The arithmetic set

console.log(7 + 3, 7 - 3, 7 * 3);  // 10 4 21
console.log(7 / 2);                // 3.5 — division always allows decimals
console.log(7 % 2);                // 1   — remainder: "is 7 odd?" is 7 % 2 === 1
console.log(2 ** 10);              // 1024 — exponent (not ^ — that is bitwise XOR)

% (remainder) is more useful than it looks: cycles, "every 5th item", even/odd checks.

The + operator decides: text or math

Decision diagram: if either operand of + is a string, the other is converted and the values are joined as text; otherwise numeric addition

The + operator's decision, in full. One rule explains the whole "1" + 2 family of surprises.

console.log(1 + 2);        // 3
console.log("1" + 2);      // "12" — a string wins, everything joins as text
console.log(1 + 2 + "3");  // "33" — left to right: 1+2=3, then "3"+"3"
console.log("6" - 1);      // 5    — - has no text meaning, so it converts!

The habit: never rely on these conversions. Convert explicitly with Number("6") / String(6) and the behaviour is always yours.

Equality

Strict versus loose — run the lab

console.log("5" == 5);    // true  — == CONVERTS first, then compares
console.log("5" === 5);   // false — === compares type AND value
console.log(0 == "");     // true  — both convert to 0. A bug factory.
console.log(null == undefined);  // true  — one special-case pair

== has a page of conversion rules in the spec; === has one sentence. The professional rule: use === and !== everywhere.

NaN — and how to test it

console.log(NaN === NaN);         // false — NaN equals nothing, itself included
console.log(Number.isNaN(0 / 0)); // true — the ONLY correct NaN test

Truthiness

Every value is secretly true or false

Inside an if, any value converts to true or false. The false team has exactly six members — memorise the list; everything else is truthy:

// falsy: 0, "" (empty string), null, undefined, NaN, false
console.log(Boolean(0), Boolean(""), Boolean("0")); // false false TRUE — "0" is text!

&& and || return values, not just answers

// && returns the first falsy side (or the last value if all truthy):
console.log(user && user.name);   // if user is null, the expression stops there

// || returns the first truthy side — the classic "default value" trick:
const port = settings.port || 8080;

?? — defaulting that respects zero

const volume = 0;
console.log(volume || 50); // 50 — WRONG: 0 is falsy, so the fallback fired
console.log(volume ?? 50); // 0  — RIGHT: ?? only falls back on null/undefined

If you get this instead: a setting of 0 or "" silently ignored — a || was used where ?? was meant. One character, two different defaulting styles.

?. — read deep without crashing

const city = user?.address?.city; // undefined instead of a TypeError
                                  // when user or address is null/undefined

?. short-circuits the whole chain to undefined when the left side is nullish. It replaces pages of if (user && user.address && ...).

Numbers are floats

IEEE-754 in practice

console.log(0.1 + 0.2);                  // 0.30000000000000004 — not YOUR bug
console.log(0.1 + 0.2 === 0.3);          // false
console.log((0.1 * 10 + 0.2 * 10) / 10); // 0.3 — the standard money fix

Every number is a 64-bit float; decimals are approximations. Never compare computed floats with === — round, or work in whole units (cents).

Parsing and converting

console.log(Number("12.5"));   // 12.5 — strict: "" → 0, "abc" → NaN
console.log(parseInt("12px")); // 12   — reads what it can from the start
console.log(parseInt("08", 10)); // 8 — ALWAYS pass the radix 10

Number() for clean input, parseInt(s, 10) for leading-number strings, Number.isNaN to check the result. BigInt (an n suffix: 10n) handles integers beyond Number.MAX_SAFE_INTEGER — rare on day one, vital for IDs and finance.

Practice: the coercion lab

The task

Run foundations/10_coercion_lab.js with node. Before each output appears, say the expected value out loud. Keep a personal "surprises" list — that list is your actual curriculum.

The checklist

  • You can explain the + decision from the diagram without looking.
  • You can name all six falsy values.
  • You know when || betrays you and ?? does not.
  • You can explain why 0.1 + 0.2 !== 0.3 and what to do about money.