DEV Community

Cover image for const πŸ“¦, seal 🀐, freeze ❄️ & immutability πŸ€“ in JS
Benjamin Mock
Benjamin Mock

Posted on • Updated on • Originally published at

const πŸ“¦, seal 🀐, freeze ❄️ & immutability πŸ€“ in JS

Immutable data can not be changed once created. Most of the times this leads to simpler and less error-prone code. That's why immutable data structures are quite often a hot topic. Let's see what we can achieve with JS!

So const for the win you might say, because this way you create constants, right? Well ... no. With const, you create a variable, that can't be reassigned.

So the following code won't work:

const x = "Dog"
x = "Cat" // crashes with "Uncaught TypeError: Assignment to constant variable."

With let and var it's possible to reassign variables, of course.

So, but why isn't const immutable then? Let's see what happens, when we use an object instead of a primitive value.

const obj =  { name: "Doggo" }
// let's change a property = "Kitty"
// and add one
obj.animal = "cat"

console.log(obj) // {name: "Kitty", animal: "cat"}

// reassigning will not work
obj = { name: "Birdo" } // crashes with "Uncaught TypeError: Assignment to constant variable."

So we can still add and change properties of objects. But there is a seal method and a freeze method on Object, that basically do exactly what they say. Let's have a look at seal first:

const obj =  { name: "Doggo" }
// let's seal our object
// let's change the property again = "Kitty"
// and also add one again
obj.animal = "cat"

console.log(obj) // {name: "Kitty"}

So what happened here? The name-property could be changed, but the animal property could not be added. That's exactly what seal does: it prevents properties to be added to an object. Existing properties can still be changed.

The freeze method prevents both, changing as well as adding/deleting properties.

const obj =  { name: "Doggo" }
// let's freeze our object
// let's change the property again = "Kitty"
// and also add one again
obj.animal = "cat"

console.log(obj) // {name: "Doggo"}

Okay, so using const in combination with Object.freeze will give us immutability, right? Well ... again no. The freeze method is no so called deep freeze. This means, that only your first level object is actually frozen; objects within this object aren't. Let's have a look at an example:

// we'll give the dog an owner, that also has a name (mine ;) 
const obj = { name: "Doggo", owner: { name: "Ben" }Β }
// we'll again freeze the object

// and this time we'll change the name of the owner = "Bla"

console.log(obj) // {name: "Doggo", owner: {name: "Bla"}}

To actually achieve immutability you could create a deep freeze method, to recursivly runs to all object properties and freezes all nested objects. Let me know if you're interested in a tutorial on this!

Or you can use a library like Immutable.js

Want to get better at Web Development?
πŸš€πŸš€πŸš€subscribe to my weekly βœ‰οΈnewsletter

Discussion (10)

adam_cyclones profile image
Adam Crockett

I have a wishlist for JavaScript around this subject.

I have always wanted the ability to temporarily toggle mutability.

I would love to see the removal of the const assignment from the language altogether and make everything immutable by default, making let the new const and var the new let.

Anything immutable should be deeply immutable by default not just the reference to them.

I realize that would make this post redundant, but the fact that it's actually hard to do immutable JavaScript (compared to say, Rust Lang) and you have to explain it, meaning it's complex, there is something wrong with the implementation of es6. That's just my opinion. Anyway great post 😁.

juancarlospaco profile image
Juan Carlos

You can do that using Nim (is immutable by default):

adam_cyclones profile image
Adam Crockett

Nim, I hear good things... But I am already proficient with typescript and I am currently learning rust, do you think Nim is going to stick around. I personally can understand why Nim was invented, what are its goals?

Thread Thread
juancarlospaco profile image
Juan Carlos

For Frontend only?, is kinda explained on the link,
better types than TypeScript, immutable by default,
powerful meta-programming, fullstack with 1 lang,
for Frontend is kinda like Svelte but with TypeScript and a whole Backend ecosystem, JS/WASM/WebGL you name it.

For Backend, easy syntax like Python or Ruby, is fast as C.

For DevOps, it can run interpreted cross-platform.

If you are doing Rust+TypeScript you are mixing 2 very different worlds,
I do everything with 1 lang.

Different from Rust, Nim is self-supporting Rust is not,
Nim can produce Hardened Binaries yet Rust can not,
has new memory management strategy that works kinda similar to Rust,
you can "import" a C/C++/ObjC/JS lib no need to re-write,
Nim also has an LLVM backend like Rust too,
Nim uses a lot less lines of code (expected from a C++ alternative),
not trolling Rust just explaining diff :)

adam_cyclones profile image
Adam Crockett

Ooo that does look good. I wouldn't want to give up rust though, it's really a great language.

moopet profile image
Ben Sinclair

I was looking at these the other day and think they're a disaster waiting to happen. Why do they let you try to change things you can't change without throwing an exception? When trying to reassing a const does?

MDN says

Attempting to delete or add properties to a sealed object, or to convert a data property to accessor or vice versa, will fail, either silently or by throwing a TypeError (most commonly, although not exclusively, when in strict mode code).

Which is just about as vague as you can get. It's, "do this, and that might happen. YOLO."

adam_cyclones profile image
Adam Crockett

It means if you don't use strict mode it will fail silently but as most transpiler tools will allow the option to compile in strict mode it's just a Boolean away. Or you can just add strict mode yourself. Then catching exceptions becomes a useful idea in JavaScript.

frehner profile image
Anthony Frehner

Yeah! I even just wrote an article exploring how strict mode affects Freeze-ing objects and calling functions and stuff, since I just barely had to dive into it. Hope it helps :)

fpresencia profile image
Francisco Presencia

I created a deep freeze method just like this! For a similar article I wrote last week. The snippet is part of my React state management library Statux:

// Deep freeze any object
const freeze = obj => {
  // Does not need freezing
  if (typeof obj !== "object") return obj;  // Already frozen
  if (Object.isFrozen(obj)) return obj;  // Freeze props recursively before freezing self
  for (let key of Object.getOwnPropertyNames(obj)) {
    if (Array.isArray(obj) && key === "length") continue;
    obj[key] = typeof obj[key] === "object"
      ? freeze(obj[key])
      : obj[key];
  return Object.freeze(obj);

To use it:

const user = freeze({
  name: 'Sarah',
  friends: ['John']

user.friends[0] = 'Tim';
// Error as expected πŸŽ‰