Freezing Objects in JavaScript
By: Chris Hosler
Tags: JavaScript, Javascript Development, Software Development

As we edge closer to winter and the temperatures outside begin to get colder and colder, I bring you a relative topic in Javascript, Freezing Objects.

Objects in JavaScript are, essentially, containers for storing things, and in their natural state, we can always add new items into that container, change things in the container, or take stuff out of the container. Well, what if we want to put the lid on the container, so that nothing new can go inside it and nothing can come out? Now, your first thought may be to assign it with the const keyword, and I can understand that line of thought, however, it doesn’t quite solve our problem. Defining an object with const means that we cannot reassign that object’s name later in our code…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; anObject = {

new: "object"

}; // TypeError: Assignment to constant variable.

…it still allows us to add things into our object…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; anObject.a = 10;

anObject.b = "Goodbye";

anObject.c[2] = 2;

anObject.d.f = 30;

anObject.g = true; console.log(anObject); // Outputs {a: 10, b: 'Goodbye', c: [ 0, 1, 2 ], d: {e: 10, f: 30}, g: true}

As you can see, we are still able to add new things to our container, for more information on how this works see my article on the differences between var , let , and const .

So how do we solve our problem? Introducing… Object.freeze() ! With Object.freeze() we can lock, or freeze, our object so that changes cannot be made to it, meaning that you can no longer add, change, or remove properties from our object. Let’s take a look…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; Object.freeze(anObject); anObject.g = 10; console.log(anObject); // Outputs { a: 1, b: 'Hello', c: [ 0, 1 ], d: { e: 10, f: 20 } }

// Our anObject remains unchanged

So by simply calling the Object.freeze() method on our object we can put the lid on our container, that way if we try to add a new property to our anObject we can’t, and we get back our frozen object (Note: if you are using strict mode you may receive a TypeError ). Just for demonstration purposes, this also is true if we try to change a property…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; Object.freeze(anObject); anObject.a = 10000; console.log(anObject); // Outputs { a: 1, b: 'Hello', c: [ 0, 1 ], d: { e: 10, f: 20 } }

// Our anObject still remains unchanged

…or if we try to remove a property…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; Object.freeze(anObject); delete anObject.a; console.log(anObject); // Outputs { a: 1, b: 'Hello', c: [ 0, 1 ], d: { e: 10, f: 20 } }

// Our anObject cannot be changed

As a quick aside, Object.freeze() can also be used for arrays…

const anArray = [ 0, 1, 2, 3 ]; Object.freeze(anArray); anArray[0] = 1000; console.log(anArray); // Outputs [ 0, 1, 2, 3 ]

…and if we try to remove an element…

const anArray = [ 0, 1, 2, 3 ]; Object.freeze(anArray); anArray.pop(); console.log(anArray); // TypeError: Cannot delete property '3' of [object Array]

Now things get a little interesting, whenever we use the Object.freeze() method, it only freezes the top level of our anObject so any nested objects or arrays that we have in our anObject can still be changed…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; Object.freeze(anObject); anObject.c[0] = 2;

delete anObject.d.f;

anObject.d.g = "new"; console.log(anObject); // Outputs { a: 1, b: 'Hello', c: [ 2, 1 ], d: { e: 10, g: 'new' } }

// Our anObject HAS BEEN changed!

The question then becomes, is it possible to freeze everything within our anObject ? The answer is that, yes we can, it just takes a little bit of working. In order to freeze everything inside of our anObject we need to apply a little bit of Recursion to go through our anObject , check to see if any of it’s properties are an object, and if the property is an object apply the Object.freeze() method…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: 10, f: 20}

}; function freezeEverything(object) {

Object.keys(object).forEach(property => {

if (typeof object[property] === 'object')

freezeEverything(object[property]);

});

return Object.freeze(object);

}; freezeEverything(anObject); anObject.b = "anything";

anObject.c[0] = 1000; console.log(anObject); // Outputs { a: 1, b: 'Hello', c: [ 0, 1 ], d: { e: 10, f: 20 } }

// anObject now cannot be changed at all, completely frozen!!

Let’s walk through what is going on here. First, we are creating a new function freezeEverything that takes the argument of an object. freezeEverything then collects all of the properties that belong to the passed object, using the Object.keys() method. Next, we use the forEach method to go over, or iterate through, all of those collected properties. Then, if the property is an object, we again call on the freezeEverything function passing the property as the argument. The final step is to use the Object.freeze() method on any of the objects that work their way through the freezeEverything function, to well, freeze everything.

This will work even if we have more nested objects…

const anObject = {

a: 1,

b: "Hello",

c: [0, 1],

d: {e: {f: 1, g: 2}}

}; freezeEverything(anObject); // using our function from above anObject.d.e.f = 500; console.log(anObject) // Outputs { a: 1, b: 'Hello', c: [ 0, 1 ], d: { e: 10, f: 20 } }

So there you go, if you have an object and you want to make sure that the contents of that object cannot be changed, you can the Object.freeze() method to lock it down, and you can also utilize recursion to make sure that even if there are nested objects, things will stay the way you want them.

I hope that you find this useful and as always thanks for reading!!