Operators & Coercion
==, ===, ??, ?., and the floating-point gotcha.
Operators are the punctuation of JavaScript. They look harmless, but a few of them have famously weird behavior because JS will quietly convert types behind your back. This lesson teaches the operators you'll use every day and the coercion rules that trip up everyone exactly once.
Arithmetic operators
The boring math ones first, because there are still surprises:
5 + 3 // 8
10 - 4 // 6
3 * 4 // 12
10 / 3 // 3.3333333333333335
10 % 3 // 1 (remainder)
2 ** 10 // 1024 (exponent)
let count = 0;
count++; // post-increment (returns old, then adds)
++count; // pre-increment (adds, then returns)The + operator does double duty: it adds numbers and it glues strings together. This is where coercion starts to bite.
Comparison: == vs ===
JS has two equality operators. They look almost the same but behave very differently:
// Loose equality (==) - converts types before comparing
0 == "0" // true
0 == "" // true
null == undefined // true
1 == true // true
// Strict equality (===) - types must match
0 === "0" // false
null === undefined // false
1 === true // false=== and !==. The only exception worth knowing: x == null is a clean way to check for both null and undefined at once.The famous coercion gotchas
JavaScript's type coercion has a few rules that produce results nobody would design today. They're in the language forever, so let's look at them head-on:
// + with a string: everything becomes a string
1 + "1" // "11"
"5" + 3 // "53"
[] + [] // "" (both arrays become "")
[] + {} // "[object Object]"
{} + [] // 0 (parsed as a block + array - yes, really)
// Math operators that aren't + coerce to number
"5" - 3 // 2
"5" * "2" // 10
"abc" - 1 // NaN
// NaN is never equal to itself
NaN === NaN // false
Number.isNaN(NaN) // true (use this)
// Truthy/falsy values
Boolean(0) // false
Boolean("") // false
Boolean(null) // false
Boolean(undefined)// false
Boolean(NaN) // false
Boolean("0") // true ⚠ a string "0" is truthy
Boolean([]) // true ⚠ empty array is truthy
Boolean({}) // true ⚠ empty object is truthyfalse, 0, "", null, undefined, NaN. Everything else is truthy, including "0", [], and {}.Logical operators
&& and ||don't return booleans. They return one of their operands. This is how short-circuiting works:
// || returns the first truthy value (or the last if all falsy)
"" || "default" // "default"
0 || 42 // 42
"hi" || "fallback" // "hi"
// && returns the first falsy value (or the last if all truthy)
"hi" && 42 // 42
0 && doSomething() // 0 - doSomething never runs
// Negation
!true // false
!!"hi" // true (the "to-boolean" trick)Nullish coalescing: ??
|| is too aggressive when you only want to fall back on null or undefined. The ?? operator was added for exactly that:
const count = 0;
const a = count || 10; // 10 - wrong! 0 is a valid value
const b = count ?? 10; // 0 - only falls back if null/undefined
const name = "" ?? "anon"; // ""
const real = "" || "anon"; // "anon"Reach for ?? when zero, empty string, or false are valid values you want to keep.
Optional chaining: ?.
Reading a property on undefined throws a TypeError. The ?. operator short-circuits to undefined instead:
const user = { name: "Ada" };
user.address.street // TypeError
user.address?.street // undefined - safe
// Works on function calls and array access too
user.greet?.() // undefined if greet is missing
arr?.[0] // safe array access
// Pair it with ?? for a default
const street = user.address?.street ?? "unknown";Template literals
Backticks create strings that can span lines and embed any expression with ${...}:
const name = "Ada";
const age = 36;
const intro = `Hello, ${name}! You are ${age * 12} months old.`;
// "Hello, Ada! You are 432 months old."
const multiline = `line one
line two
line three`;Try the gotchas live
Open the console and play. Try changing values, mixing types, breaking things on purpose. The fastest way to learn coercion is to watch it happen.
=== and move on.Quiz
What does [] + {} evaluate to?
Recap
- Use
===by default.==coerces and lies. - Six falsy values:
false,0,"",null,undefined,NaN. Everything else is truthy. ??falls back only on nullish.||falls back on any falsy.?.safely reads through possibly-undefined chains.- Template literals beat string concatenation for readability.
NaN === NaNis false. UseNumber.isNaN.