Cloud Pak for Integration

 View Only

Immutability in JavaScript

By Aritra Das Bairagya posted Tue February 21, 2023 12:49 AM

  

In this article, we'll discuss the immutability concept and the various implementation of immutability with use cases. According to the MDN definition, An immutable value is one whose content cannot be changed without creating an entirely new value. Let's understand:

Mutable: These are the objects whose state can be modified after it is created.

Immutable: These are the objects whose state cannot be changed once the object is created.

First of all, there is nothing wrong with mutability or if our code is mutable. But the worry is the misuse of mutability, which can cause a lot of issues including several side effects to our application. Immutability is just a pattern of coding or maintaining the state of an object, making development simple, traceable, and testable. The main goal is, an update should not modify any existing object, but rather create a completely new object.



But why we are so serious about the implementation of immutability? and we already know that javascript (ES6) has const, by which we can do the same.
const will provide us with immutable functionality by default, right?

All right, let's discuss, it’s about assigning a variable to a new value, or reassignment. Declared a variable using "let" or "var", it’s possible to reassign variables and values. However, when we try with "const", it'll create issues. Consider the below example one with "let" and another with "const", we are assigning a value "red" into the "color" variable, and whenever we are trying to modify it, it acts differently, which is actually what we are looking for, right?

The interesting thing is declaring anything with const doesn’t mean it’s immutable. const means we are not allowed to reassign something. According to MDN,

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned.



Mutable and Immutable Data Types:

In JavaScript:

  • All the primitive values are by default immutable. Once a primitive value is declared or created, we cannot change it.
  • Objects and arrays are mutable by default. All the properties and elements can be changed without reassigning a new value.

Primitive data types:
There are 7 primitive data types:

  • string.
  • number.
  • bigint.
  • boolean.
  • undefined.
  • symbol.
  • null.

Objects and Arrays:
As we discussed above, these are mutable by default. In the below example, we are declaring an object called "color" with some attributes & values with it, such as the “font” attribute having a “red” value. And whenever we are trying to add some other attribute (i.e. "hover") or change any existing attribute’s value (such as the “font” attribute’s value to “green”), it works.

Now take an example of an array we are trying to modify like the above and it also works.
Let's consider another excellent example of mutation:
Here "animal" is a mutable object, whenever we're making any changes in the "animal", it automatically reflects in the "aliens" object as both are pointing to the same reference. And not only for objects, but there are some mutator methods present in arrays also, like:
  • copyWithin
  • fill
  • pop
  • push
  • reverse
  • shift
  • sort
  • splice
  • unshift
But considering "map" and "filter", which are immutable by design as they create a new array without mutating the original array while iterating or processing. For example:
Here is the list of Non-mutator array methods:
  • slice()
  • join()
  • includes()
  • filter()
  • concat()
  • every()
  • find()
  • findIndex()
  • map()
  • reduce()
  • some()
  • flat()
  • flatMap()

Immutability is our requirement:
Developers could find some situations where they may not want an object to change programmatically, hence, we want to make it immutable. When something is immutable, we can't add something new to it, modify it, or delete any existing property (if we have one). There is no way even to extend it.

Considering most of the modern javascript-based framework encourages developers to consider immutability as best practice when required.



Benefits:
There are a couple of benefits of having immutability in JavaScript, here are some:

  • Improved readability, because with this all the variables stay the same, and the code will be simpler to understand.
  • Traceability or change tracking, easily trace how and where our data is changing.
  • Easier testing, since the state of our immutable object doesn’t change, it’ll be much easier to test the code and track the bugs.
  • Performance benefits (some of the cases, where memory uses are a concern).


Solution: 

There are a few popular solutions to achieve this.

  • Object.freeze // recommended approach
  • Object.defineProperty

Object.freeze

We can freeze (make immutable) an object/ array or anything using the function Object.freeze(obj). Using this we are telling that nobody can change any values or properties if the object is frozen. The object passed to the freeze method will become immutable.

In the above example, we are using Object.freeze(color) to freeze all the properties of the "color" object, now our objects will not be having mutation issues. And we are pretty much in sync with the MDN definition, An immutable value is one whose content cannot be changed without creating an entirely new value. Here is how we achieved immutability. But there is one catch:
When we are freezing any object, it works like a shallow freeze. This means, only the top-level or root-level properties are frozen. Any of the property's values are another object, which means that all the inner objects are not frozen. We still can make changes to it.
Let’s understand with an example:
consider the below example, we have an object named color and we have some attributes on it, such as "hover", "font", and "bg". After declaration, we are freezing the "color" object and try to modify the  “font” (top-level or root-level) attribute's value to “green”. In this case, we are not allowed to make any changes according to the simple freeze() method definition and which works fine in this case.
But if we consider the below example with the same object defined above, whenever we’re trying to modify any nested property such as "bg" > “top” (from value "white" to "cyan") under the “bg” property, our freeze will allow modifying.
Now the question is how we can freeze or achieve immutability on a whole object or all its properties. Here we need something called deep freeze, JavaScript doesn’t provide any default implementation for this hence we can use our customize recursion solution to achieve this (or else we can use some additional libraries from npm).
So, we can iterate through every property and recursively apply the freeze method to everything. It will make sure that the nested objects are also frozen:

const deepFreeze = obj => {
Object.keys(obj).forEach(prop => {
    if (typeof obj[prop] === 'object' && !Object.isFrozen(obj[prop])) {
      deepFreeze(obj[prop]);
  }
  });
  return Object.freeze(obj);
};
Now let's try with the same example as above. Try to modify the nested property "bg" > “top” (from value "white" to "cyan") under the “bg” property, now our freeze should not allow modifying.
And now quickly discuss the second option:

Object.defineProperty

This (static) method has a "writable" property that takes a boolean value, so if we make it false, the object will act like read-only. See the below example:



There are two other methods are also available with similar kinds of functionality.

freeze(), seal(), preventExtentions()
With Object.freeze we achieve full immutability whereas the seal() and preventExtentions() methods provide partial immutability:

  • Object.seal - Adding new property or deleting existing property is not allowed, however modification of existing property is allowed.
  • Object.preventExtensions - Adding a new property is not allowed. However, modification of existing property or deletion of existing property is allowed.



Note: All the example codes have been written in the codepen (https://codepen.io/) platform with strict mode.

0 comments
61 views

Permalink