a.k.a. “pointers for JavaScript developers”

On Day 1 of learning to code, someone tells you “A variable is like a box. Writing thing = 5 puts 5 in the thing box”. And that’s not really how variables work, but it’s good enough to get you going. It’s like in math class when they lie to you about the full picture, because the full picture would explode your brain right now.

Some time later though, you start to see weird problems. Variables changing when you didn’t change them. Ghosts in the machine.

“I thought I made a copy of that! Why did it change?” <– that right there is a reference bug!

By the end of this post, you’ll understand why that happens and how to fix it.

What is a Reference?

References are everywhere in JS, but they’re invisible. They just look like variables. Some languages, like C, call these things out explicitly as pointers, with their own syntax to boot. But JS doesn’t have pointers, at least not by that name. And JS doesn’t have any special syntax for them, either.

Take this line of JavaScript for example: it creates a variable called word that stores the string “hello”.

let word = "hello"

the word variable pointing at a box containing the string hello

Notice how word points to the box with the “hello”. There’s a level of indirection here. The variable is not the box. The variable points to the box. Let that sink in while you continue reading.

Now let’s give this variable a new value using the assignment operator =:

word = "world"

What’s actually happening here isn’t that the “hello” is being replaced by “world” – it’s more like an entirely new box is created, and the word is reassigned to point at the new box. (and at some point, the “hello” box is cleaned up by the garbage collector, since nothing is using it)

If you’ve ever tried to assign a value to a function parameter, you probably realized this doesn’t change anything outside the function.

The reason this happens is because reassigning a function parameter will only affect the local variable, not the original one that was passed in. Here’s an example:

function reassignFail(word) {
  // this assignment does not leak out
  word = "world"
}

let test = "hello"
reassignFail(test)
console.log(test) // prints "hello"

Initially, only test is pointing at the value “hello”.

Once we’re inside the function, though, both test and word are pointing at the same box.

Two variables with the same value

After the assignment (word = "world"), the word variable points at its new value “world”. But we haven’t changed _test_. The test variable still points at its old value.

After reassigning word

This is how assignment works in JavaScript. Reassigning a variable only changes that one variable. It doesn’t change any other variables that also pointed at that value. This is true whether the value is a string, boolean, number, object, array, function… every top-level variable works this way.

Two Types of Types

JavaScript has two broad categories of types, and they have different rules around assignment and referential equality. Let’s talk about those.

Primitive Types in JavaScript

There are the primitive types like string, number, boolean (and also symbol, undefined, and null). These ones are immutable. a.k.a. read-only, can’t be changed.

When a variable holds one of these primitive types, you can’t modify the value itself. You can only reassign that variable to a new value.

The difference is subtle, but important!

Said another way, when the value inside a box is a string/number/boolean/symbol/undefined/null, you can’t change the value. You can only create new boxes.

It does not work like this…

This is why, for example, all of the methods on strings return a new string instead of modifying the string, and if you want that new value, you’ve gotta store it somewhere.

let name = "Dave"
name.toLowerCase();
console.log(name) // still capital-D "Dave"

name = name.toLowerCase()
console.log(name) // now it's "dave"

#javascript #developer

A Visual Guide to References in JavaScript
2.10 GEEK