DEV Community

Abdullah Atta
Abdullah Atta

Posted on

Constants In JavaScript (and all that bulls**t)

If you have been programming in JavaScript for a little while you'd have noticed that const in JavaScript does not mean you can't change it. There's also no memory, performance or any other benefit. WTF!

Okay, okay. Look at this piece of code:

const toastStatus = "burnt"; // why though?
// let's change this
toastStatus = "not ok";
Enter fullscreen mode Exit fullscreen mode

You will be expecting that it will give an error, right? Yup. You are right. It does give this error:

Uncaught TypeError: Assignment to constant variable.

So far so good. Let's say we want to give an id to the toast status:

const toast = {
    id: "da_best_toast",
    status: "burnt"
};
Enter fullscreen mode Exit fullscreen mode

And something happens and this toast suddenly becomes unburnt (accidentally of course):

toast.status = "all ok"
Enter fullscreen mode Exit fullscreen mode

You'd be an expecting the same old error, right? But guess what? No error. Not even a hint. It's all okay (as the toast said). WTF! Same is the case with any Object (i.e. Arrays, Objects, Classes, Functions etc. etc.)

WTF!!

Okay, okay. I guess that's a good enough rant for now. SO what's the problem?

The Problem

Nothing is immutable in JavaScript. Nothing is unchangable in JavaScript. Anything can become anything in JavaScript. That's why you have TypeScript but even then there are some quirks in JavaScript that will make you pull your hair out.

So what do we do if we want perfect immutability? Such that no one can change the object. At all?

You could use a third-party library like: Immutable-Js or immer. But why? Why, why, why? When JavaScript can do it but for some reason doesn't do it by default (via the const keyword)?

If you haven't noticed by now: Objects are extensible in JavaScript. Meaning you can add, delete or edit without any restriction. Any Object. Even the standard ones like global or window. Now, I understand that that is a cool thing and all but it has many, many disadvantages. So let's try to make our objects...constant.

The Solution..s

Object.freeze:

From MDN:

The Object.freeze() method freezes an object. A frozen object can no longer be changed; freezing an object prevents new properties from being added to it, existing properties from being removed, prevents changing the enumerability, configurability, or writability of existing properties, and prevents the values of existing properties from being changed. In addition, freezing an object also prevents its prototype from being changed. freeze() returns the same object that was passed in.

I think you got the point. It makes your object unchangable. Forever.

Here's the same example as above:

const toast = Object.freeze({
    id: "da_best_toast",
    status: "burnt"
});
Enter fullscreen mode Exit fullscreen mode

Now if you wanted to change the status like this:

toast.status = "all ok"
Enter fullscreen mode Exit fullscreen mode

It will give this error (be a good kid and turn on strict mode):

TypeError: Cannot assign to read only property 'done' of object '#<Object>'

And later if you accidentally add another property:

toast.bread = "baba bread";

Enter fullscreen mode Exit fullscreen mode

It will give another error:

TypeError: Cannot add property bread, object is not extensible

And then if you try and delete the status (accidentally, of course):

delete toast.status
Enter fullscreen mode Exit fullscreen mode

You'd get:

TypeError: Cannot delete property 'done' of #<Object>

In this way you can make any Object immutable. You could also write a utility function like this, to make it a bit flashier:

function im(obj){
    return Object.freeze(obj)
}

// and then use it like so:

const immutable = im({ data: "some_data" });
Enter fullscreen mode Exit fullscreen mode

And if you wanted to check whether an object is frozen, simply do:

Object.isFrozen(toast); // === true
Enter fullscreen mode Exit fullscreen mode

Now what if you wanted to only disallow new properties from being added/deleted but allow existing properties from being changed. In other words, what if you wanted "partial" immutability?

Well, for that you have:

Object.seal:

From MDN:

The Object.seal() method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. Values of present properties can still be changed as long as they are writable.

So basically, it will only allow existing properties from being edited (not deleted). Let's see an example.

const network = Object.seal({
    stable: true,
    speed: "50mbps",
    status: "connected"
});

// let's suppose, the network is disconnected suddenly, you'd simply do:
network.status = "disconnected";

// however if you wanted to add upstream speed, it will throw:
network.upstream = "25mbps";

// or if you try and delete, it will also throw.
delete network.stable
Enter fullscreen mode Exit fullscreen mode

You can easily check if an object is sealed or not using:

Object.isSealed(network); // === true
Enter fullscreen mode Exit fullscreen mode

So far so good. But what if you wanted to disallow only additions but allow edits and deletions? For that you have Object.preventExtensions.

Object.preventExtensions

From MDN:

The Object.preventExtensions() method prevents new properties from ever being added to an object (i.e. prevents future extensions to the object).

Let's see an example:

const person = Object.preventExtensions({
    name: "Baker Yota",
    age: 35,
});

// now let's say you wanted to add a new property, it will throw.
person.father = "Samhil Yota"

// But you can edit the age:
person.age = 36;

// And delete it too:
delete person.age;
Enter fullscreen mode Exit fullscreen mode

And if you wanted to check whether an object is extensible or not, simply do:

Object.isExtensible(person); // === false
Enter fullscreen mode Exit fullscreen mode

So there you have it: Immutability in JavaScript.

Notes:

  1. Objects that are manipulated in this way do not have their references altered. So frozen_object === real_object gives true.
  2. You can put in any kind of Object. Be it an Array, Function, Class etc.

Conclusion:

Javascript is a nice language but it does have some wacky areas a beginner would not be expecting. Hopefully, this little lesson in immutability will have made you understood that area of "science". Now don't be annoying and give me a reaction. :D Just joking. But seriously, do leave a comment with your thoughts.

Thanks for reading,
thecodrr

P.S. Here is my latest project in case you are interested:

GitHub logo thecodrr / fdir

⚡ The fastest directory crawler & globbing library for NodeJS. Crawls 1m files in < 1s

The Fastest Directory Crawler & Globber for NodeJS

The Fastest: Nothing similar (in the NodeJS world) beats fdir in speed. It can easily crawl a directory containing 1 million files in < 1 second.

💡 Stupidly Easy: fdir uses expressive Builder pattern to build the crawler increasing code readability.

🤖 Zero Dependencies*: fdir only uses NodeJS fs & path modules.

🕺 Astonishingly Small: < 2KB in size gzipped & minified.

🔥 All Node Versions Supported: Unlike other similar libraries that have dropped support for Node versions < 10, fdir supports all versions >= 6.

🖮 Hackable: Extending fdir is extremely simple now that the new Builder API is here. Feel free to experiment around.

* picomatch must be installed manually by the user to support globbing.

Support

Do you like this project? Support me by donating, creating an issue, becoming a stargazer, or opening a pull request. Thanks.

Top comments (0)