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
obj.name = "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
Object.seal(obj)
// let's change the property again
obj.name = "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
Object.freeze(obj)
// let's change the property again
obj.name = "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
Object.freeze(obj)
// and this time we'll change the name of the owner
obj.owner.name = "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
Top comments (9)
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 ๐.
You can do that using Nim (is immutable by default):
github.com/nim-lang/Nim/wiki/Nim-f...
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?
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 :)
Ooo that does look good. I wouldn't want to give up rust though, it's really a great language.
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
Which is just about as vague as you can get. It's, "do this, and that might happen. YOLO."
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.
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. dev.to/frehner/exploring-javascrip... Hope it helps :)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.