Meeting the Primitive Values
episode 05Until now, we have been observing our JavaScript universe from the surface of our planet. We have familiarized ourselves with the values that populate our universe from a distance, but in this module, we’re changing that. We’re going to hop in a spaceship and go exploring, introducing ourselves to every value in our JavaScript universe.
Spending the time to look at each value in detail might feel unnecessary, but you can only say there are “two apples” when you clearly see that they’re two distinct apples. Distinguishing values from one another is key to understanding equality in JavaScript—which will be our next topic.
Our spaceship will guide us through the “celestial spheres” of JavaScript to meet different values. We’ll meet the primitive values first: Booleans, Numbers, Strings, and so on. Later, we’ll meet Objects and Functions. Consider it a sightseeing tour.

Undefined
We’ll start our tour with the Undefined type. This is a very straightforward place to start, because there is only one value of this type—undefined.
console.log(typeof(undefined)); // "undefined"

It’s called undefined, so you might think it’s not there—but it is a value, and a very real one! Like a black hole, undefined is grumpy and can often spell trouble. For example, reading a property from it will break your program:
let person = undefined;console.log(person.mood); // TypeError!
Oh, well. Luckily, there is only one undefined in our entire JavaScript universe. You might wonder: why does it exist at all? In JavaScript, it represents the concept of an unintentionally missing value.
You could use it in your own code by writing undefined—like you write 2 or "hello". However, undefined also commonly “occurs naturally.” It shows up in some situations where JavaScript doesn’t know what value you wanted. For example, if you forget to assign a variable, it will point to undefined:
let bandersnatch;console.log(bandersnatch); // undefined

Then you can point it to another value, or to undefined again if you want.
Don’t get too hung up on its name. It’s tempting to think of undefined as some kind of variable status, e.g. “this variable is not yet defined.” But that’s a completely misleading way to think about it! In fact, if you read a variable that was actually not defined (or before the let declaration), you will get an error:
console.log(jabberwocky); // ReferenceError!let jabberwocky;
That has nothing to do with undefined.
Really, undefined is a regular primitive value, like 2 or "hello".
Handle it with care.
Null

The next stop on our tour is Null. You can think of null as undefined’s sister; there is only one value of this type—null. It behaves very similarly to undefined. For example, it will also throw a fuss when you try to access its properties:
let mimsy = null;console.log(mimsy.mood); // TypeError!

null is the only value of its own type. However, null is also a liar. Due to a bug in JavaScript, it pretends to be an object:
console.log(typeof(null)); // "object" (a lie!)
You might think this means null is an object. Don’t fall into this trap! It is a primitive value, and it doesn’t behave in any way like an object.
Unfortunately, typeof(null) is a historical accident that we’ll have to live with forever.
In practice, null is used for intentionally missing values. Why have both null and undefined? This could help you distinguish a coding mistake (which might result in undefined) from valid missing data (which you might express as null). However, this is only a convention, and JavaScript doesn’t enforce this usage. Some people avoid both of them as much as possible!
I don’t blame them.
Booleans

Next on our tour, we’ll meet booleans! Like day and night or on and off, there are only two boolean values: true and false.
console.log(typeof(true)); // "boolean"console.log(typeof(false)); // "boolean"
We can perform logical operations with them:
let isSad = true;let isHappy = !isSad; // The oppositelet isFeeling = isSad || isHappy; // Is at least one of them true?let isConfusing = isSad && isHappy; // Are both true?
Before continuing our tour of the JavaScript universe, let’s check our mental model. Use the sketchpad below or a piece of paper and draw the variables (remember to think of them as wires) and the values for the above lines of code.
Check your answer against this picture:

First, verify that isHappy points to false, isFeeling points to true, and isConfusing points to false. (If you got different answers, there is a mistake somewhere along the way—walk through each line step by step.)
Next, verify that there is only one true and one false value on your sketch. This is important! Regardless of how booleans are stored in the memory, in our mental model there are only two of them.
Numbers

So far, we have introduced ourselves to four values: null, undefined, true, and false.
Hold on tight, as we add eighteen quintillion, four hundred and thirty-seven quadrillion, seven hundred and thirty-six trillion, eight hundred and seventy-four billion, four hundred and fifty-four million, eight hundred and twelve thousand, six hundred and twenty-four more values to our mental model!
I am, of course, talking about numbers:
console.log(typeof(28)); // "number"console.log(typeof(3.14)); // "number"console.log(typeof(-140)); // "number"
At first, numbers might seem unremarkable, but let’s get to know them a little better!
Math for Computers
JavaScript numbers don’t behave exactly the same way as regular mathematical numbers do. Here is a snippet that demonstrates it:
console.log(0.1 + 0.2 === 0.3); // falseconsole.log(0.1 + 0.2 === 0.30000000000000004); // true
This might look very surprising! Contrary to popular belief, this doesn’t mean that JavaScript numbers are broken. This behavior is common in different programming languages. It even has a name: floating-point math.
You see, JavaScript doesn’t implement the kind of math we use in real life. Floating-point math is “math for computers.” Don’t worry too much about this name or how it works exactly. Very few people know about all its subtleties, and that’s the point! It works well enough in practice that most of the time you won’t think about it. Still, let’s take a quick look at what makes it different.
Colors and Numbers
Have you ever used a scanner to turn a physical photo or a document into a digital one? This analogy can help us understand JavaScript numbers.
Scanners usually distinguish at most 16 million colors. If you draw a picture with a red crayon and scan it, the scanned image should come out red too—but it will have the closest red color our scanner picked from those 16 million colors. So if you have two red crayons with ever so slightly different colors, the scanner might be fooled into thinking their color is exactly the same!
We can say that a scanner uses colors with limited precision.
Floating-point math is similar. In real math, there is an infinite set of numbers. But in floating-point math, there are only 18 quintillion of them. So when we write numbers in our code or do calculations with them, JavaScript picks the closest numbers that it knows about—just like our scanner does with colors.
In other words, JavaScript uses numbers with limited precision.
We can imagine all of the JavaScript numbers on an axis. The closer we are to 0, the more precision numbers have, and the closer they “sit” to each other:
This is because relatively small numbers occur more often in our programs, and we usually want them to be precise.
As we move from 0 in either direction, we start losing precision. At some point, even two closest JavaScript numbers stay further apart than by 1:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 (again!)console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994console.log(Number.MAX_SAFE_INTEGER + 4); // 9007199254740996console.log(Number.MAX_SAFE_INTEGER + 5); // 9007199254740996 (again!)
Luckily, any whole numbers between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER are exact. This is why 10 + 20 === 30.
But when we write 0.1 or 0.2, we don’t get exactly 0.1 and 0.2. We get the closest available numbers in JavaScript. They are almost exactly the same, but there might be a tiny difference. These tiny differences add up, which is why 0.1 + 0.2 doesn’t give us exactly the same number as writing 0.3.
If this is still confusing, don’t worry. You can learn more about floating-point math, but you already know more than I did when I started writing this guide! Unless you work on finance apps, you likely won’t need to worry about this.
Special Numbers
It is worth noting that floating-point math includes a few special numbers. You might occasionally run into NaN, Infinity, -Infinity, and -0. They exist because sometimes you might execute operations like 1 / 0, and JavaScript needs to represent their result somehow. The floating-point math standard specifies how they work, and what happens when you use them.
Here’s how special numbers may come up in your code:
let scale = 0;let a = 1 / scale; // Infinitylet b = 0 / scale; // NaNlet c = -a; // -Infinitylet d = 1 / c; // -0
Out of these special numbers, NaN is particularly interesting. NaN, which is the result of 0 / 0 and some other invalid math, stands for “not a number.”
You might be confused by why it claims to be a number:
console.log(typeof(NaN)); // "number"
However, there is no trick here. From a JavaScript perspective, NaN is a numeric value. It is not null, undefined, a string, or some other type. But in the floating-point math, the name for that term is “not a number”. So it is a numeric value. It happens to be called “not a number” because it represents an invalid result.
It’s uncommon to write code using these special numbers. However, they might come up due to a coding mistake. So it’s good to know they exist.
BigInts

If numbers expanded our JavaScript universe, the next stop on our tour, BigInts, will really keep us busy exploring. In fact, we could explore them forever!
Regular numbers can’t represent large integers with precision, so BigInts fill that gap (literally). How many BigInts are there in our universe? The specification says they have arbitrary precision. This means that in our JavaScript universe, there is an infinite number of BigInts—one for each integer in math.
If this sounds strange, consider that you’re already comfortable with the idea of there being infinite integers in math. (If you’re not, give it a few moments!) It’s not much of a leap then from a “math universe” to a “JavaScript universe.”
(And from there, we can go straight to the Pepsi Universe.)
You can see the idea of arbitrary precision illustrated here:
let alot = 9007199254740991n; // n at the end makes it a BigInt!console.log(alot + 1n); // 9007199254740992nconsole.log(alot + 2n); // 9007199254740993nconsole.log(alot + 3n); // 9007199254740994nconsole.log(alot + 4n); // 9007199254740995nconsole.log(alot + 5n); // 9007199254740996n
No funny business with the rounding! BigInts are great for financial calculations where precision is especially important.
But keep in mind that nothing is free. Operations with truly huge numbers may take time and resources—we can’t fit all the possible BigInts inside the computer memory. If we tried, at some point it would crash or freeze. But conceptually, we could tour our JavaScript universe for eternity and never reach every single BigInt.
BigInts were only recently added to JavaScript, so you won’t see them used widely yet and if you use an older browser, they won’t work.
Strings

Our next tour stop is Strings, which represent text in JavaScript. There are three ways to write strings (single quotes, double quotes, and backticks), but they refer to the same concept. These three string literals result in the same string value:
console.log(typeof("こんにちは")); // "string"console.log(typeof('こんにちは')); // "string"console.log(typeof(`こんにちは`)); // "string"
An empty string is a string, too:
console.log(typeof('')); // "string"
Strings Aren’t Objects
All strings have a few built-in properties.
let cat = 'Cheshire';console.log(cat.length); // 8console.log(cat[0]); // "C"console.log(cat[1]); // "h"
This doesn’t mean that strings are objects! String properties are special and don’t behave the way object properties do. For example, you can’t assign anything to cat[0]. Strings are primitives, and all primitives are immutable.
A Value for Every Conceivable String
In our universe, there is a distinct value for every conceivable string. Yes, this includes your grandmother’s maiden name, the fanfic you published ten years ago under an alias, and the script of Matrix 5 which hasn’t been written yet.
Of course, all possible strings can’t literally fit inside a computer memory chip. But the idea of every possible string can fit inside your head. Our JavaScript universe is a model for humans, not for computers!
This might prompt a question. Does this code create a string?
// Try it in your consolelet answer = prompt('Enter your name');console.log(answer); // ?
Or does it merely summon a string that already exists in our universe?
The answer to this question depends on whether we’re studying JavaScript “from the outside” or “from the inside.”
Outside our mental model, the answer depends on a specific implementation. Whether a string is represented as a single block of memory, multiple blocks, or a special data structure like a rope, is up to the JavaScript engine.
But as we discussed in Studying from the Inside, we have agreed to talk about the JavaScript universe as if we lived inside of it. We won’t make statements about it that we can’t verify from inside the universe—by running some code.
The question of whether a string already “exists” or is “created” is not something we can test from the code. Inside our mental model, this question doesn’t mean anything. We can’t set up an experiment to say whether strings “get created” or “get summoned” within our JavaScript universe.
To keep our mental model simple, we will say that all conceivable string values already exist from the beginning—one value for every distinct string.
Symbols
We’ve already explored quite a bit of our JavaScript universe, but there is just one more (quick) stop on the first part of our tour: Symbols.
let alohomora = Symbol();console.log(typeof(alohomora)); // "symbol"
It’s important to know that Symbols exist, but it’s hard to explain their role and behavior without diving deeper into objects and properties. Symbols serve a similar purpose to door keys: they let you hide away some information inside an object and control which parts of the code can access it. They are also relatively rare, so in this tour of our universe, we’re going to skip them. Sorry, symbols!
To Be Continued
Now that we’ve met all of the primitive values, we’ll take a small break from our tour. Let’s recap the primitive values we’ve encountered so far!

- Undefined
- Null
- Booleans
- Numbers
- BigInts
- Strings
- Symbols
We’ve also learned a few interesting facts about JavaScript numbers:
- Not all numbers can be perfectly represented in JavaScript. Their decimal part offers more precision closer to
0, and less precision further away from it. - Numbers from invalid math operations like
1 / 0or0 / 0are special.NaNis one of such numbers. They may appear due to coding mistakes. typeof(NaN)is a number becauseNaNis a numeric value. It’s called “Not a Number” because it represents the idea of an “invalid” number.
We will continue our sightseeing tour by meeting the non-primitive values, specifically, objects and functions.
Exercises
This module also has exercises for you to practice!
Don’t skip them!
Even though you’re likely familiar with the concept of primitive values, these exercises will help you cement the mental model we’re building. We need this foundation before we can get to more complex topics.