Mutability and reassignability in JavaScript
Tags: beginners, javascript, tips
Published: 2020-07-10 14:14:02

When you started learning JavaScript and were introduced to variable declaration, you might have read something along these lines:

`let` _creates a variable that can change and be reassigned freely._ `const`, _on the other hand, creates a read-only variable that cannot be changed._

Then, as if an afterthought, you might have seen something like this:

_It is important to remember, though, that even if they are declared using_ `const` _arrays and objects can still change._

At which point you would hardly be to blame should you be confused. This is just an example, by the way. Your case might be different. You might have got an amazing explanation that perfectly cleared up how `let` and `const` worked.

Nonetheless, `let` and `const` tend to generate confusion as to whether the value they hold can change or not. The goal of this article is to try a different approach and perhaps clear up some of the confusion.

The first thing that you need to know is that `let` and `const` have nothing to do with whether a value can be changed or not.

## Mutability depends on type

Let this sink for a moment. Whether you can change a value has nothing to do with the keyword you used to declare its variable.

So, what does it have to do with, then? The answer is very simple: the value's type.

In JavaScript there are two kinds of types: primitive types and objects. If you really want to dig deep into the issue, [MDN has a good explanation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures). But for now, it is enough to grossly oversimplify it and say that objects are object literals and arrays and primitives are numbers, strings, booleans, undefined, null and symbols.

### Primitive types

Primitive types are immutable in JavaScript. This means that their value can never be changed. `5` is always going to be `5`, you can't simply add something to it and change it. The code below exemplifies it:

```javascript
let x = 5
x + 2
console.log(x) // -> 5
```

Even if we add two to `x`, the value of `x` is still `5`. The expression `x + 2` doesn't change the value `5`. It generates a new value (which in this case happens to be `7`). If we wanted to use that new value, we would need to assign it to something (more on that later).

Similarly the String method [slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice) (and all other String methods) returns a new string rather than modifying the value of a string.

```javascript
let str = 'Blue mountain'
str.slice(0, 4)
console.log(str) // -> 'Blue mountain'
```

In summary, the value of a primitive type cannot be changed, so it is immutable. Once it's defined, that's what it is. We can create a new value based on that, but it'll be a new one, not the same.

### Objects (and Arrays)

Objects (and arrays) are mutable. This means that their value can change. In fact, [arrays have a plethora of methods you can use to change their value](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).

Let's use the Array method [push](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) as an example:

```javascript
let arr = [ 1, 2, 3 ]
arr.push(4)
console.log(arr) // -> [ 1, 2, 3, 4 ]
```

As we can see here, the actual value inside `arr` has changed. Whereas in the previous examples `x` and `str` always had the same value, even after we operated on them, `arr`'s value has changed.

We can similarly extend object literals:

```javascript
let obj = { a: 1 }
obj.b = 2
console.log(obj) // -> { a: 1, b: 2 }
```

Again, the value inside `obj` has changed, or to be more precise, mutated. This is what mutability means. The same value can take a new shape and become something different.

At this point, I have a little confession to make. I have been using _change_ as synonymous to _mutate_. This isn't strictly true and it isn't always used that way. _Change_'s meaning is broader and, as far as I'm aware, it's not a technical term. So often you'll see it used in the meaning of _reassign_. _Change_'s ambiguity is, I think, one of the reasons for the confusion about mutability.

## So what's the deal with `let` and `const`?

Ah, I'm glad you asked. `let` and `const` are not related to mutability. They are used to express reassignability.

In short, a variable declared with `let` is reassignable, a variable declared with `const` isn't.

### What is this reassignability?

By reassignability I mean the ability to be assigned an entirely new value. That is, completely discard whatever value we had before and take on a new one.

In JavaScript, when we want to assign a value we use the [assignment operator (=)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment) (well, [almost always at any rate](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Increment)). That means that when we want to assign a value to a variable, we use an `=`, like so:

```javascript
let a = 1
let b = 3
```

Reassignability means that after the first assignment, a variable can be reassigned a new value. Its current value may or may not be mutable, that depends on its type. However, if it is reassignable, it can always completely discard said value and take on a new one. Even of a different type. The following code is perfectly valid:

```javascript
let x = 5
x = [ 1, 2, 3 ]
x = 'Blue mountain'
```

In this case, `x` is first assigned the value `5` and then **reassigned** several new values. The values themselves don't change (although the array might potentially change). What changes is what `x` **is assigned to**.

Even if we were to run `x = x + 2` we're not really mutating the value `5`. What we are doing is returning a new value from an expression **and then reassigning** that new value to `x`.

### `const`, `let` and objects

Because objects are mutable, we can always mutate their value (unless we [somehow prevent it](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)). When we declare them using `let`, we're allowing the object to be discarded and have a new value assigned to the variable.

```javascript
1  let obj = { a: 1 }
2  obj.b = 2
3  obj = { c: 3 }
4  console.log(obj) // -> { c: 3 }
```

In this example, we are declaring the object `obj`, mutating it to add a new property in the next line, and then assigning a completely new object to it. We can run line 2 because objects are mutable. If they weren't, it wouldn't work. We can run line 3 because `obj` is reassignable. It is reassignable because we declared it using `let`.

```javascript
1  const obj = { a: 1 }
2  obj.b = 2
3  obj = { c: 3 } // -> throws TypeError
4  console.log(obj)
```

Here line 3 throws an error because `obj` is not reassignable. We can mutate the value as much as we want. Objects are mutable. But we cannot discard the value and assign a new one to it because we declared it using const.

## Summary

The keywords `let` and `const` are used to describe a variable's reassignability, not whether the value is mutable. Mutability depends on the value's type.

Primitive values are immutable and therefore cannot take a different shape. `5` is always `5`. Objects (and arrays) are mutable and therefore their shape can be modified.

`let` allows us to reassign a new value to a variable. We can discard whatever value it had before and give it a new one. `const` doesn't. Variables declared with `const` can only be assigned a value once.