Freeze and Seal Objects in JavaScript

Variables we declare in javascript with the help of const, are not purely constant. Let’s say that if we have a variable called config with a bunch of properties and if we print it to the console you will see it has a name and a database object.

const config = {
    name: "module-account",
    database: {
        host: "127.0.0.1",
        port: "2020",
        username: "admin",
        password: "r@@t",
    },
};

console.log(config);    // {"name":"module-account","database":{"host":"127.0.0.1","port":"2020","username":"admin","password":"r@@t"}}

But if we update the value of let’s say name to be xyz, you will see you can do that. Although it’s a constant.

config.name = "xyz";

console.log(config.name);   // xyz 

To prevent this javascript comes with a bunch of methods, such as Object.freeze, Object.seal and Object.preventExtensions. Which we can use to make them immutable. Let’s look at the examples to understand how they work and how we can use them in our codebase.

Object.freeze

If we freeze an object, let’s say Object.freeze(config) and print the name you will see that we are still able to read the value from the config.

Object.freeze(config);

console.log(config.name);       // xyz

But if we try to update any of the existing values, let’s say config.name is abc, we will get the error that we cannot assign the value to a read-only property.

config.name = "abc";    // error 

Similarly, if we try to delete a property, let’s say delete config.name, we will not be able to do that, and also if we try to add a new property, let’s say config.timeout is 3, we will still get the error because the object is not extensible.

delete config.name;     // error
config.timeout = 3;     // error

The only thing we can do is reading the properties from the existing object. One important thing to remember about the freeze is that it works only at the top level. So now, in this case, we have a database object, which is nested inside the config object.

If we try to update the value for, let’s say config.database.host is 10.10.10.20 and if we print the config, you will see that the database host has been updated.

config.database.host = "10.10.10.20";

console.log(config.database.host);      // 10.10.10.20

If we want the object to be fully frozen, with all the objects inside, we have to recursively freeze all the objects. So in this case now if we want the database to be frozen also, we will have to do

Object.freeze(config.database);

And now we’ll get the error while updating the host that the database host cannot be updated because config.database is frozen

config.database.host = "10.10.10.20";     // error

Object.seal

Next, we have Object.seal which is similar to Object.freeze in a way that you cannot add or remove properties from an object but you can update the values of the existing properties. Let’s say that we seal our config object so Object.seal(config).

And now, if we do config.name to be XYZ, you will see that the name has been updated.

config.name = "XYZ";

console.log(config.name);

But if we try to delete the property from the config object. Let’s say delete config.database, we will not be able to do that because, with seal, you cannot delete the properties from the object. And also, if we try to add a new property, let’s say config.timeout is 3 we will get the error, that you cannot add a new property to the object.

delete config.database;     // error
config.timeout = 3;     // error

And similar to object.freeze, object.seal also works on the top-level only. So, the seal will not be applied to the database object here and if we try to delete a property from the database object, let’s say delete config.database.host, we will see that the database host has been deleted from here.

delete config.database.host;        // success

So, if we want to prevent this also, we will have to seal the nested objects.

Object.seal(config.database);

And now we will get the error that we cannot delete a property from a sealed object.

Object.preventExtensions

The last one we have is the Object.preventExtensions, which allows us to update the values and delete the properties from an existing object but it does not allow us to add new properties to the object.

So now, if we call Object.preventExtensions on our config object, and try to update the value for one of the properties, let’s say name, you will see that the name has been updated, and also if we try to delete one of the properties, let’s say delete config.database, we can also delete the properties but if we try to extend our object or let’s say add new properties, for example, config.timeout is 3 we can’t do that because our object is not extensible.

config.name = "XYZ";        // success
delete config.database;     // success

config.timeout = 3;         // error

One more thing to know about the preventExtensions is that if you delete a property from an object, you cannot add the same property back again and the reason for that is because adding a new property is considered extension. So if I do config.database again with something, it will give me the error that you cannot add a new property to the object.

config.database = {host: "10.20.20.10"};        // error

Similar to freeze and seal, preventExtensions also applies only to the top-level properties.

There are three more methods that can be used to check if the objects are frozen, sealed, or extensible.

Helper Methods

So Object.freeze is to freeze the objects and Object.isFrozen can be used to check if the object is frozen or not.

const user1 = {firstName: "John"};
const user2 = {firstName: "Doe"};

Object.freeze(user1);

console.log(Object.isFrozen(user1));        // true
console.log(Object.isFrozen(user2));        // false

Object.seal is to seal and Object.isSealed is to check if the object is sealed or not. And for the Object.preventExtensions, we have Object.isExtensible which can be used to check if the new properties can be added to the object or not.

Conclusion

We can conclude this topic using a CRUD table.

  CREATE READ UPDATE DELETE
Object.freeze
Object.seal
Object.preventExtensions

And that wraps it up for this article. Feel free to leave your feedback or questions in the comments section.