DEV Community

SavagePixie
SavagePixie

Posted on

Mutability and reassignability in JavaScript

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. 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:

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 (and all other String methods) returns a new string rather than modifying the value of a string.

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.

Let's use the Array method push as an example:

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:

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 (=) (well, almost always at any rate). That means that when we want to assign a value to a variable, we use an =, like so:

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:

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). When we declare them using let, we're allowing the object to be discarded and have a new value assigned to the variable.

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.

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.

Latest comments (2)

Collapse
 
mashaeldev profile image
Mashael

That's very helpful thank you 👏👏

Collapse
 
pentacular profile image
pentacular

The insight here should be that the variable is not its value.