Constants, Object.freeze, Object.seal and Immutable in JavaScript

Introduction

In this article, we're diving into Immutable and Mutable in JavaScript, while also exploring how functions like Object.freeze() and Object.seal() relate to Immutable. This is a pretty important topic that can be super helpful in projects with complex codebase.

So, what's Immutable?

Immutable is like a property of data in JavaScript. It means once the data is created, it can't be changed. This allows for better memory management and catching changes promptly. This is a big deal and totally contrasts with Mutable, which is the default nature when you initialize data in JavaScript.

Implementing Immutable in a project makes development much smoother, reduces issues that crop up, and saves a ton of effort and time for maintenance down the road.


But what happens if you don't use Immutable?

Let me give you an example of how changing data during development can lead to some serious issues.

let info = {
name: "name",
address: "address"
};

function doSomeThing() {
if (info.name = "Name to compare") { // change value
// handle something
}
info = { name: "Another name" }; // change value
// do something else
}

doSomeThing(info);
console.log(info);

Here's a simple example to show how data can easily be changed. You might not see a big problem here, but imagine in a large project with lots of complex functions. Changing data like this can hide many risks, leading to unnecessary issues and significantly increasing the time spent tracking issues and maintaining the code.


Using constants (const)

When a variable is defined as a constant, it means you can't assign a different value to that variable. However, it's important to note that in JavaScript, there are reference types, and even a constant variable's reference value can still be changed.

For example:

const v1 = {a: 1, b: '2', c: [1, 2, 3]}
// v1 = 2 // TypeError: Assignment to constant variable
v1.a = 3 // work
v1.c.push(4) // work
v1['d'] = true // work
delete v1.b // work
console.log(v1)

const v2 = [1, 2, 3]
v2.push(4) // work
console.log(v2)

As you can see, even when using constants, the fields within an object can still have their values changed, and new fields can be added or existing ones removed. Generally, data types like object (including array) remain mutable.


Using Object.freeze()

This is a built-in function provided by JavaScript to prevent modifications, additions, or deletions of existing fields within an object.

Let's take a look at an example:

const v3 = {a: 1, b: '2', c: [1, 2, 3]}
Object.freeze(v3)
const v3 = Object.freeze({a: 1, b: '2', c: [1, 2, 3]}) // the same with above

// v3 = 2 // TypeError: Assignment to constant variable
// v3.a = 4 // Cannot assign to read only property 'a' of object
v3.c.push(4) // work
// v3['d'] = true // Cannot add property d, object is not extensible
// delete v3.b // Cannot delete property 'b' of #<Object>
console.log(v3)

You can see there's a significant improvement in immutability when using Object.freeze() compared to just using constants. However, for reference types, they can still be altered.

In JavaScript, there's also the Object.isFrozen function provided to check if a variable is a frozen object.


Using Object.seal()

With the built-in function Object.seal, it only prevents adding new properties or deleting existing ones, but it still allows changing the value of properties.

const v5 = {a: 1, b: '2', c: [1, 2, 3]}
Object.seal(v5)
// v4 = 2 // TypeError: Assignment to constant variable
v5.a = 4 // work
v5.c.push(4) // work
// v4['d'] = true // Cannot add property d, object is not extensible
// delete v4.b // Cannot delete property 'b' of #<Object>
console.log(v5)

Similarly to Object.freeze, Object.seal has the Object.isSealed function to check if an object is sealed.


Using the immer library

Let me introduce you to the immer library, which provides comprehensive immutable capabilities, suitable for both Front-end and Back-end development. It's a fantastic library, straightforward to use, and highly recommended by many developers.

Installation: npm i immer

After that, you can use this library very simply following the instructions below:

import {Immer, produce} from 'immer'

const immer = new Immer() // default {autoFreeze: true})
const v1 = {a: 1, b: '2', c: [1, 2, 3]}
const v2 = immer.produce(v1, draft => {
draft.c.push(3, 4) // could do anything with draft value
return draft
})
// v2.b = '3' // freeze object, Cannot assign to read only property 'b' of object '#<Object>'

const v3 = produce(v2, draft => {
draft.a = 2
return draft
})

const immer2 = new Immer({autoFreeze: false})
const v4 = immer2.produce(v1, draft => {
draft.c.push(3, 4)
return draft
})
v4.b = '3' // normal object, could be re-assign

console.log('v1', v1)
console.log('v2', v2)
console.log('v3', v3)
console.log('v4', v4)


Conclusion

In this article, I introduced the concept and provided you with examples related to using const, Object.freeze, and Object.seal to support you in implementing immutability in JavaScript.

You can combine constants with Object.freeze or Object.seal to create immutable objects, as these are features readily supported by JavaScript.

However, if you want absolute immutability, then you should use the immer library.

Happy coding!

Comments

Popular posts from this blog

A Handy Guide to Using Dynamic Import in JavaScript

What is react-query? Why should we use react-query?

Explaining Async/Await in JavaScript in 10 Minutes

Using styled-components in React/Next Applications

Enhance Security for Node.js Applications

Understanding React Server Component

Installing PostgreSQL with Docker

Sitemap

Using PureComponent and React.memo to Improve Performance in React