Counting the
Values · I
We tour the JavaScript sky and count its inhabitants — undefined, null, the two booleans, and the eighteen quintillion numbers. The point of counting is to learn what makes one value distinct from another. That is the whole secret to equality, which comes next.
The further from zero, the wider the gaps
JavaScript numbers have limited precision, like a scanner that knows only sixteen million colors. Drag the magnitude up. Watch the distance between two adjacent representable numbers grow — until even consecutive integers can no longer all be stored.
Studying from the inside
We describe the JavaScript world on its own terms — not how an engine stores bytes. Each value is just a value, never a "pointer" or a "memory cell".
There are two ways to study this universe. From the outside, you ask how the engine "really" works — that a string is a run of bytes on a silicon chip. We take the other road. We stand inside the universe and observe its laws like physicists, without asking whether it's "real".
Why bother? Because the implementation answer changes constantly — sometimes mid-program. If you heard a tidy story about how JavaScript "really" represents numbers or objects, it is probably wrong. So set aside intuitions about passing by reference, stack allocation, and copy-on-write. The foundation is simpler: the world is full of values, each value has a type, some types are primitive (and therefore immutable), and variables are wires.
"Are the stars still there when I blink? I shrug. Implementation details." A value is good enough. Don't let memory metaphors distract you from an accurate, high-level model.
The lonely values: undefined & null
Two types have exactly one value each. They look like absence, but they are real values — and real troublemakers.
Undefined
The Undefined type has a single value: undefined. Despite the name, it is very much there. Like a black hole, it spells trouble — reading a property off it breaks your program:
let person = undefined; console.log(person.mood); // TypeError!
It means an unintentionally missing value. It shows up on its own when JavaScript doesn't know what you wanted — for example, a declared-but-unassigned variable points at it. Don't read it as "not yet defined", though. Reading a genuinely undeclared name is a different failure entirely:
// declared, unassigned → undefined let bandersnatch; console.log(bandersnatch); // undefined // never declared → ReferenceError, unrelated to undefined console.log(jabberwocky); // ReferenceError! let jabberwocky;
Null
null is undefined's sister. It also throws a fuss when you touch its properties, and it is the only value of its type. It is also a liar:
console.log(typeof(null)); // "object" — a lie!
Do not fall for it. null is a primitive and behaves nothing like an object. typeof null is a historical accident we live with forever. By convention, null marks an intentionally missing value — which lets you distinguish a bug (likely undefined) from valid empty data (null). The language does not enforce this; some people avoid both. I don't blame them.
This is the single most famous bug in the language. null is primitive. The string "object" is the lie; the behaviour is the truth.
Booleans: only two of them
Like day and night, the Boolean type has exactly two values: true and false.
let isSad = true; let isHappy = !isSad; // the opposite → false let isFeeling = isSad || isHappy; // at least one true? → true let isConfusing = isSad && isHappy;// both true? → false
Sketch the wires for the snippet above. How many distinct boolean values appear on your diagram?
Four variables, but only onetrue and onefalse in the whole universe. isHappy and isConfusing both land on the same false; isSad and isFeeling both land on the same true. Regardless of how booleans are stored, in our model there are only two of them.
Numbers: a math for computers
JavaScript numbers are floating-point. There are about eighteen quintillion of them, and they crowd together near zero and spread apart far from it.
This snippet surprises everyone the first time:
> console.log(0.1 + 0.2 === 0.3); > console.log(0.1 + 0.2 === 0.30000000000000004);
This is not a JavaScript bug — it is floating-point math, the same in most languages. Think of a scanner: it knows at most sixteen million colours, so two almost-identical reds get rounded to the same one. JavaScript treats numbers the same way. Real math has infinitely many numbers; floating-point has only ~18 quintillion, so it snaps every value to the nearest one it knows.
Precision is densest near 0 and thins out as you move away — exactly what the lab above shows. Eventually two neighbouring numbers sit more than 1 apart, so not every integer survives:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 (!) console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994
Every whole number between MIN_SAFE_INTEGER and MAX_SAFE_INTEGER is exact — which is why 10 + 20 === 30. But 0.1 and 0.2 are already the nearest available numbers, not exactly themselves, and those tiny errors accumulate.
Special numbers
Floating-point also defines a few oddities for results like 1 / 0. You rarely write them on purpose, but a mistake can summon them:
| expression | result | note |
|---|---|---|
1 / 0 | Infinity | too big to represent |
-1 / 0 | -Infinity | the negative end |
0 / 0 | NaN | invalid math |
1 / -Infinity | -0 | yes, negative zero |
NaN — "not a number" — is the strangest. It claims to be a number:
console.log(typeof(NaN)); // "number"
No trick here. From JavaScript's view NaN is a numeric value — not a string, not null. It is simply the number that represents an invalid numeric result. We will meet it again in Equality, where it breaks one of the rules.
Recap · the count so far
- Undefined: one value,
undefined— unintentionally missing. - Null: one value,
null— intentionally missing, andtypeof nulllies "object". - Booleans: two values,
trueandfalse. - Numbers: one value per floating-point number — denser near 0, sparser far away.
- Numbers from invalid math (
1/0,0/0) are special:NaN,Infinity,-Infinity,-0.typeof NaNis"number".
Counting is really about distinctness — knowing when two things are the same value or two different ones. In Part II we finish the tour (BigInts, strings, objects, functions) and then cash it all in on equality.