DEV Community

Sai Charan
Sai Charan

Posted on

Immutability in JavaScript

Before I address the topic of Immutability, lets actually find out what mutation is.

Mutation

Anything that changes/transforms the behavior or the structure of an object is called mutation. Like the force mutating Goku in the above picture.

Mutation in JavaScript

The definition might seem pretty similar to the above example. Although, in JavaScript, it looks something like this,

let a = 1;

a = 2;

console.log(a); // 2

//In the above case, we are mutating(transforming) the value of the variable a.
Enter fullscreen mode Exit fullscreen mode

In the above case, we are mutating(transforming) the value of the variable a.

But, Why is mutation a problem in JavaScript?

Before I answer this question, let me talk about a few concepts about Pass by Value and Pass by Reference in JavaScript.

Pass by Value

Consider the following example,

let a = 5;
let b = a;

console.log(a); // 5
console.log(b); // 5

a = 15;
console.log(a); // 15
console.log(b); // 5
Enter fullscreen mode Exit fullscreen mode

In the above example, I create a variable “a” with a value of 5 and assign it to a variable called “b”. Then, the value of “a” as well as “b” would be 5. In this case, I have passed the value of “a” to “b”.

Now let's say I change the value of “a” to 15. In doing so, my “b” value does not get changed because it was passing initially by a value. This way of passing the values of variables to another variable can be stated as pass by value.

Pass by Reference

Consider the following example,

let object1 = {name: 'sai'};
let object2 = object1;

console.log(object1); // {name: 'sai'}
console.log(object2); // {name: 'sai'}

object1.name = 'charan';

console.log(object1); // {name: 'charan'}
console.log(object2); // {name: 'charan'} WHAAAAAT?
Enter fullscreen mode Exit fullscreen mode

In the above example, I create an object and assign it to the variable “object1”. I create another variable “object2” and assign “object1” to it.

Now in line number 7, I mutate “object1”’s name property to “charan”. Surprisingly, when I log the value of “object2” the name property of “object2” has also changed to “charan”. So what exactly happened here? Objects are usually passed by reference. Since object2 is equated to object1, it is passed by reference, meaning, they both hold the same memory location. So, when we change the value of object1, object2 usually gets changed.

Both object1 and object2 point to the same memory location

Both object1 and object2 point to the same memory location.

We can find a similar problem in case of arrays as well. Consider the below example,

let arr = [1,2,3,4,5];
let newArr = arr;

console.log(arr); // [1,2,3,4,5]
console.log(newArr); // [1,2,3,4,5]

arr.push(6);

console.log(arr); // [1,2,3,4,5,6]
console.log(newArr); // [1,2,3,4,5,6] WHAAAAAT?
Enter fullscreen mode Exit fullscreen mode

Both arrays and objects are passed by reference.

How is this a problem? Imagine, you pass an object/array across various functions, and you mutate it at some point, the same object/array might be mutated in other places, leading to unexpected behavior in some parts of the code, resulting in bugs.

Solution: Going Immutable!

One way of achieving immutability in JavaScript is by using Object.assign.

let object1 = {name: 'sai'};
let object2 = Object.assign({}, object1, {name: 'charan'});

console.log(object1); // {name: 'sai'}
console.log(object2); // {name: 'charan'}

object1.name = 'batman';

console.log(object1); // {name: 'batman'}
console.log(object2); // {name: 'charan'}
Enter fullscreen mode Exit fullscreen mode

In the above example, we are using Object.assign to create “object2” variable. Object.assign usually takes in three values, an initial value, the source object and the replacing object. In our case the initial value is an empty object, we get the contents of “object1”, and to that, we add a new object {name: ‘charan’}. If there is already a property ‘name’ in object1, it gets replaced with the existing value(in our case, ‘charan’) and assigns it to the initial value(which is an empty object). So now, my object2 is a fresh object. The mutations of “object1” do not affect object2, since now both refer to different memory locations.

Another way to achieve immutability in JavaScript is by using the spread operator.

let object1 = {name: 'sai'};
let object2 = {...object1, name: 'charan'};

console.log(object1); // {name: 'sai'}
console.log(object2); // {name: 'charan'}

object1.name = 'batman';

console.log(object1); // {name: 'batman'}
console.log(object2); // {name: 'charan'}
Enter fullscreen mode Exit fullscreen mode

The above example does the same thing as Object.assign, but the syntax looks a lot cleaner.

In arrays, immutability can be achieved by using the same spread operator.

let arr = [1,2,3,4,5];
let newArr = [...arr, 6];

console.log(arr); // [1,2,3,4,5]
console.log(newArr); // [1,2,3,4,5,6]

arr.push(11);

console.log(arr); // [1,2,3,4,5,11]
console.log(newArr); // [1,2,3,4,5,6]
Enter fullscreen mode Exit fullscreen mode

In the above example, 6 gets added to the “newArr” variable. The mutations of “arr”, as in the line number 7, does not affect “newArr” variable. Also, for arrays in JavaScript, map, filter, reduce can be used to avoid mutations since they return new arrays every-time they are called.

Writing immutable Javascript code is a good practice. I hope this article gives you a basic idea of how variables are stored in memory and how immutability helps you write better quality code. Feedback and suggestions are welcome. :)

Article available on Medium too

Top comments (0)