DEV Community

Cover image for Immutability and you: Part 1
Alain D'Ettorre
Alain D'Ettorre

Posted on • Updated on

Immutability and you: Part 1

The problem

Consider something like this

const a = [1,2,3];
const b = a;
console.log(b); // [1,2,3,4]
Enter fullscreen mode Exit fullscreen mode

Can you see the problem? You've created an array a, then you copied (you think you copied) it into b, so you legitimately think a and b are now separate. Then you mutate array a by pushing a new value into it and then b changes as well. Why is that?!

Something like this happened:

  • A new array [1,2,3] is created inside a place in your RAM memory called the heap
  • You give this array a reference known as a, like an address, so that you can later fetch the array value from memory
  • You create a new reference b by assigning it a, so now you have two references that point to the same position in memory (red flag!)
  • The push method just mutates the array value without changing its address, so when you output b you see the unexpected. OMG. And it's just 4 lines of code.

The same problem, but with an object

const question = { content: 'What is 6x9?' };
const answer = question;
answer.content = '42.';
console.log(question); // { content: '42.' }
Enter fullscreen mode Exit fullscreen mode

When you change the content of the answer by mutating it, you change the question too (it's getting deep in here) since question and answer refer to the same value in memory.

The solution

So, how to solve the problem? Immutability!

If you think about primitives in JavaScript, like strings, numbers and booleans, you already know about immutability. Look at this

const a = 10;
const b = a;
// const a = a + 10; // You cannot reassign a const!
Enter fullscreen mode Exit fullscreen mode

As you can see, there's no way to actually mutate the number a and that's because primitives in JavaScript are immutable by default. Compound values, on the other hand, like arrays and objects, are mutable. For example, you can add values to arrays with push or even re-assign object properties (as in the example above): the values have changed, of course, but the position in memory of the whole array or object is still the same so that every variable that points to that value shares the same value.

Instead of changing just a small part of an array or object, thus mutating it, you should replace the whole thing, even if you only changed a single letter. The idea is that values should be like photographs: they're immutable, they represent some point in time and you can take as many as you want, each different even if only by a bit. If you take a bad photo of your kitten doing something funny you just take a new shot.

Updating objects and arrays "immutably" (more on that later) takes a new memory slot in the heap and the old slot just gets automatically captured and erased by a thing called garbage collector. The advantage of this is that you avoid the reference hell like in the first example, you have better testing, predictability and even time-traveling debugging.

Immutability is a key concept of Redux, which is a popular state management pattern heavily used in React and in Angular too.

The spread syntax

The spread syntax, is, really, just a bunch of dots! It's the main operator you need to finally achieve immutability. What they do is expanding what immediately follows them, creating a copy of it. Let's rewrite the first example

const a = [1,2,3];
// const b = a; // <-- We changed this
const b = [...a]; // <-- Into this
console.log(b); // [1,2,3]
Enter fullscreen mode Exit fullscreen mode

What actually happened? Basically, [...a] means "create a new array and replace ...a with all the items inside a", so you're effectively creating a real copy of a which takes a new place in memory, not just a reference to it. That's why b is now completely unaffected by whatever you do to a.

In part 2 and 3 we'll take a look at how to update arrays and objects with the spread syntax in practice

Photo by Andrea Ferrario on Unsplash

Discussion (0)