Skip to content

Values and Variables

episode 03

We’ll kick off this module with a little code snippet.

let reaction = 'yikes';
reaction[0] = 'l';
console.log(reaction);

What do you expect this code to do? It’s okay if you’re not sure. Try to find the answer using your current knowledge of JavaScript.

I want you to take a few moments and write down your exact thought process for each line of this code, step by step. Pay attention to any gaps or uncertainties in your existing mental model and write them down, too. If you have any doubts, try to articulate them as clearly as you can.

Answer
Don’t reveal until you have finished writing.
Answer

This code will either print "yikes" or throw an error depending on whether you are in strict mode. It will never print "likes".

Yikes.

Primitive Values Are Immutable

Did you get the answer right? This might seem like the kind of trivia question that only comes up in JavaScript interviews. Even so, it illustrates an important point about primitive values.

We can’t change primitive values.

I will explain this with a small example. Strings (which are primitive) and arrays (which are not) have some superficial similarities. An array is a sequence of items, and a string is a sequence of characters:

let arr = [212, 8, 506];
let str = 'hello';

We can access the array’s first item and the string’s first character similarly. It almost feels like strings are arrays:

console.log(arr[0]); // 212
console.log(str[0]); // "h"

But they’re not. Let’s take a closer look. We can change an array’s first item:

arr[0] = 420;
console.log(arr); // [420, 8, 506]

Intuitively, it’s easy to assume that we can do the same to a string:

str[0] = 'j'; // ???

But we can’t.

It’s an important detail we need to add to our mental model. A string is a primitive value, and all primitive values are immutable. “Immutable” is a fancy Latin way to say “unchangeable.” Read-only. We can’t mess with primitive values. At all.

JavaScript won’t let us set a property on any primitive value, be it a number, string or something else. Whether it will silently refuse our request or throw an error depends on which mode our code is in. But rest assured that this will never work:

let fifty = 50;
fifty.shades = 'gray'; // No!

Like any number, 50 is a primitive value. We can’t set properties on it.

Remember that in our JavaScript universe, all primitive values are distant stars, floating farthest from our code. We can point to them, but they will always stay where they are, unchanged.

I find it strangely comforting.

Variables and Values—A Contradiction?

You’ve seen that primitive values are read-only—or, in the parlance of our times, immutable. Now, use the following code snippet to test your mental model.

let pet = 'Narwhal';
pet = 'The Kraken';
console.log(pet); // ?

Like before, write down your thought process in a few sentences. Don’t rush ahead. Pay close attention to how you’re thinking about each line, step by step. Does the immutability of strings play a role here? If it does, what role does it play?

Answer
Don’t reveal until you have finished writing.
Answer

If you thought I was trying to mess with your head, you were right! The answer is "The Kraken". Immutability doesn’t play a role here.

Don’t despair if you got it wrong! This example may seem like it’s contradicting string immutability, but it’s not.

When you’re new to a language, sometimes it’s necessary to put aside contradictions so that you can avoid rabbit holes and continue learning. But now that you are committed to building a mental model, you need to question contradictions.

Contradictions reveal gaps in mental models.

Variables Are Wires

Let’s look at this example again.

let pet = 'Narwhal';
pet = 'The Kraken';
console.log(pet); // "The Kraken"

We know that string values can’t change because they are primitive. But the pet variable does change to "The Kraken". What’s up with that?

This might seem like it’s a contradiction, but it’s not. We said primitive values can’t change, but we didn’t say anything about variables! As we refine our mental model, we need to untangle a couple of related concepts:

Variables are not values.

Variables point to values.

Assigning a Value to a Variable

In our JavaScript universe, a variable is a wire that points to a value.

For example, I can point the pet variable to the "Narwhal" value. (I can also say that I’m assigning the value "Narwhal" to the variable called pet):

let pet = 'Narwhal';

But what if I want a different pet? No problem—I can point pet to another value:

pet = 'The Kraken';

All I am doing here is instructing JavaScript to point the variable, or a “wire”, on the left side (pet) to the value on the right side ('The Kraken'). It will keep pointing at that value unless I reassign it again later.

Rules of Assignment

There are two rules when we want to use the = assignment operator:

  1. The left side of an assignment must be a “wire”—such as the pet variable. Note that the left side can’t be a value. (Try these examples in the console):

    20000 = 'leagues under the sea'; // Nope.
    'war' = 'peace'; // Nope.
  2. The right side of an assignment must be an expression, so it always results in a value. Our expression can be something simple, like 2 or 'hello'. It can also be a more complicated expression—for example:

    pet = count + ' Dalmatians';

    Here, count + ' Dalmatians' is an expression—a question to JavaScript. JavaScript will answer it with a value (for example, "101 Dalmatians"). From now on, the pet variable “wire” will point to that particular value.

Fun Fact

If the right side must be an expression, does this mean that simple things—numbers like 2 or strings like 'The Kraken'—written in code are also expressions? Yes! Such expressions are called literals—because we literally write down the values that they result in.

Reading a Value of a Variable

I can also read the value of a variable—for example, to log it:

console.log(pet);

That’s hardly surprising.

But note that it is not the pet variable that we pass to console.log. We might say that colloquially, but we can’t really pass variables to functions. We pass the current value of the pet variable. How does this work?

It turns out that a variable name like pet can serve as an expression too! When we write pet, we’re asking JavaScript a question: “What is the current value of pet?” To answer our question, JavaScript follows pet “wire,” and gives us back the value at the end of this “wire.”

So the same expression can give us different values at different times!

Nitpicking

Who cares if you say “pass a variable” or “pass a value”? Isn’t the difference hopelessly pedantic? I certainly don’t encourage interrupting your colleagues to correct them. That would be a waste of everyone’s time.

But you need to have clarity on what you can do with each JavaScript concept in your head. You can’t skate a bike. You can’t fly an avocado. You can’t sing a mosquito. And you can’t pass a variable—at least not in JavaScript.

Here’s a small example of why these details matter.

function double(x) {
x = x * 2;
}
let money = 10;
double(money);
console.log(money); // ?

If we thought double(money) was passing a variable, we could expect that x = x * 2 would double the money variable.

But that’s not right: double(money) means “figure out the value of money, and then pass that value to double.” So money still points to 10. What a scam!

What are the different JavaScript concepts in your head? How do they relate to each other and how can we interact with them from code?

Write down a short list of the ones you use most often.

Putting it Together

Now let’s revisit the first example from Mental Models:

let x = 10;
let y = x;
x = 0;

I suggest that you take a piece of paper or a drawing app and sketch out a diagram of what happens to the “wires” of the x and y variables step by step.

You can do the sketch right here:

The first line doesn’t do much:

  • Declare a variable called x.
    • Draw the x variable wire.
  • Assign to x the value of 10.
    • Point the x wire to the value 10.

The second line is short, but it does quite a few things:

  • Declare a variable called y.
    • Draw the y variable wire.
  • Assign to y the value of x.
    • Evaluate the expression: x.
      • The “question” we want to answer is x.
      • Follow the x wire—the answer is the value 10.
    • The x expression resulted in the value 10.
    • Therefore, assign to y the value of 10.
    • Point the y wire to the value 10.

Finally, we get to the third line:

  • Assign to x the value of 0.
    • Point the x wire to the value 0.

After these three lines of code have run, the x variable points to the value 0, and the y variable points to the value 10.

Note that y = x did not mean point y to x. We can’t point variables to each other! Variables always point to values. When we see an assignment, we “ask” the right side’s value, and point the “wire” on the left side to that value.

I mentioned in Mental Models that it is common to think of variables as boxes. The universe we’re building is not going to have any boxes at all. It only has wires!

This might seem a bit annoying, but using wires makes it much easier to explain numerous other concepts, like strict equality, object identity, and mutation. We’re going to stick with wires, so you might as well start getting used to them now!

Our universe is full of wires.

Recap

  • Primitive values are immutable. They’re a permanent part of our JavaScript universe—we can’t create, destroy, or change them. For example, we can’t set a property on a string value because it is a primitive value. Arrays are not primitive, so we can set their properties.

  • Variables are not values. Each variable points to a particular value. We can change which value it points to by using the = assignment operator.

  • Variables are like wires. A “wire” is not a JavaScript concept—but it helps us imagine how variables point to values. When we do an assignment, there’s always a wire on the left, and an expression (resulting in a value) on the right.

  • Look out for contradictions. If two things that you learned seem to contradict each other, don’t get discouraged. Usually it’s a sign that there’s a deeper truth lurking underneath.

  • Language matters. We’re building a mental model so that we can be confident in what can or cannot happen in our universe. We might speak about these ideas in a casual way (and nitpicking is often counterproductive) but our understanding of the meaning behind the terms needs to be precise.

Exercises

This module also has exercises for you to practice!

Don’t skip them!

Even though you’re likely familiar with the concept of variables, these exercises will help you cement the mental model we’re building. We need this foundation before we can get to more complex topics.

Finished reading?
Mark this episode as learned. Progress stays in this browser.