Achieving Object Immutability in JavaScript With Freeze
Immutable Objects in JS
In this article, let’s look at a way to make objects in JavaScript immutable using the freeze method available on the Object prototype.
We will first explore what immutability is and why it is important. Then we will look at a code snippet in which we convert a normal object into an immutable one using the freeze method.
Finally, we will conclude by looking at the limitation of freeze()and also look at a quick approach to deep-freeze nested objects.
So, let’s get started.
What Is Immutability?
In the programming context, it means that the state (properties and methods) in an object are unchangeable once created. In short, making them read-only.
In JavaScript, the primitive types of number and string are immutable by design. Whereas, objects are mutable. We might be familiar that arrays are also objects in JavaScript and hence are mutable.
We could also observe a pattern here. Immutables in JS are passed by value while mutable are passed by reference.
Why Is Immutability Important?
Now, we might wonder why this concept is worth knowing and what is the advantage of making objects immutable. Let’s look at our reason:
Since objects are passed by reference, there might be scenarios where we might end up modifying the object unintentionally through one of its references. This may lead to bugs that might prove trickier to solve.
The above situation might be valid in certain use-cases and one might want to modify the source objects. But more often than not, this is something that might have occurred by mistake and is going to lead to trouble.
Assume the scenario illustrated above, in a complex web application where the reference might be available at multiple different parts of the code spanning through various conditional flows. Just that assumption alone, should have given the shivers, didn’t it?
It is always a best practice to not mutate the source, rather make a copy and modify it, if one has to.
Freezing Objects:
It is as simple as calling the Object.freeze()method and supplying the object instance to be frozen, as the parameter.
Our example snippet could be modified as follows:
Now, this code will throw an “object is not extensible” runtime error because we are trying to mutate frozen objects.
With just a line of code, we can enforce the best practice and at the same time prevent potential bugs. Doesn’t that sound like a double win?
Limitation of Freeze:
So far we have just looked at simple objects. But as developers, we are very much aware that nested objects are a reality that could be often spotted in codebases.
If one has tried the above technique on a nested object, they might have figured out that it doesn’t work quite as expected.
From the code above, we can infer that freeze is shallow.
Deep-freeze Objects:
The limitation we see above could be easily overcome through recursively freezing the nested object.
Now we can just call deepFreeze(nestedObject)instead Object.freeze(nestedObject) and it would work as expected.
Word of caution: This will end up in an infinite loop if called on cyclic references. This is a fact that one should be aware of while deep-freezing objects.
Conclusion:
In this article, we have taken a closer look at the freeze method and its limitation. We have also experimented with a recursive approach to overcome the limitation.
However, the reader should be aware that the deepFreezemethod, as seen in the code is recursive and if used on a complex object with multiple levels of nesting, might cause the stack to overflow.
This piece of writing is aimed to give the reader the underlying principle of immutability and how to achieve it without a third-party library. But if you are to use this concept in a production code you might be better off with libraries like immutable JS.