Object.freeze
Here is how you use Object.freeze:
let objectToFreeze = {
age: 28,
name: "Damien",
pets: ["Symba", "Hades", "Kiwi"],
sibling: {
age: 25,
name: "Corentin",
},
};
Object.freeze(objectToFreeze);
Object.freeze takes an object as an argument. Note that it modifies the object you pass as argument. It does not copy the object and create a new one.
So what does it mean for the object?
- You can't add new properties to the object
- You can't modify the properties if they are not objects or arrays themselves. (More on this later)
- You can't delete properties from the object
let objectToFreeze = {
age: 28,
name: "Damien",
pets: ["Symba", "Hades", "Kiwi"],
sibling: {
age: 25,
name: "Corentin",
},
};
Object.freeze(objectToFreeze);
delete objectToFreeze.age;
objectToFreeze.name = "Ben";
objectToFreeze.pets.push("Grenade");
objectToFreeze.sibling.age = 26;
objectToFreeze.lastName = "Cosset";
With the description I just gave you, you should guess what the object will look like now:
console.log(objectToFreeze)
// objectToFreeze :
{ age: 28,
name: 'Damien',
pets: [ 'Symba', 'Hades', 'Kiwi', 'Grenade' ],
sibling: {
age: 26,
name: 'Corentin'
}
}
The delete failed, modifying the name property failed and adding the lastName property failed. But modifying the array and the nested sibling object worked.
Note: If you are not in strict mode, it will fail silently. In strict mode, you will get TypeErrors
"use strict";
let objectToFreeze = {
age: 28,
name: "Damien",
pets: ["Symba", "Hades", "Kiwi"],
sibling: {
age: 25,
name: "Corentin",
},
};
Object.freeze(objectToFreeze);
delete objectToFreeze.age;
Throws this:
Shallow freeze
What we have when we call Object.freeze is a shallow freeze. We only freeze what is directly attached to the object. Properties inside objects and arrays are not concerned.
To make the entire object and everything inside immutable, you have to call Object.freeze() on every element. Here is one way you could do this:
let allYouCanFreeze = (obj) => {
// Retrieve the properties names
let propNames = Object.getOwnPropertyNames(obj);
// Loop through the properties
// If typeof is "object", meaning an array or object, use recursion to freeze its contents.
for (let name of propNames) {
let value = obj[name];
obj[name] =
value && typeof value === "object" ? allYouCanFreeze(value) : value;
}
// Finally, freeze the main object
return Object.freeze(obj);
};
Let's use it on our first object:
let objectToFreeze = {
age: 28,
name: "Damien",
pets: ["Symba", "Hades", "Kiwi"],
sibling: {
age: 25,
name: "Corentin",
},
};
allYouCanFreeze(objectToFreeze);
// Now we can't touch the pets array and the sibling object
objectToFreeze.age = 26; // Now, fails.
objectToFreeze.pets.push("Grenade"); // Now, fails
Great! Now, our sibling object and our pets array can't be modified.
Object.isFrozen
To know if an object is frozen, you can use Object.isFrozen():
Object.isFrozen(objectToFreeze); // === true
let unfrozenObj = { a: 42 };
Object.isFrozen(unfrozenObj); // === false
Object.seal
Object.seal(), like the freeze method, takes a object as an argument. Object.seal is a softer version of Object.freeze.
- You can't remove or add elements to the object.
- You can modify existing properties.
let objectToSeal = {
name: "Damien",
age: 28,
pets: ["Symba", "Hades", "Kiwi"],
sibling: {
age: 25,
name: "Corentin",
},
};
Object.seal(objectToSeal);
Pretty straightforward huh? Let's try to modify this object now:
delete objectToSeal.name;
objectToSeal.age = 56;
objectToSeal.lastName = "Cosset";
objectToSeal.sibling.age = 45;
objectToSeal.pets.push("Grenade");
Maybe you already guessed what should happened 😉
//objectToSeal new contents
{
name: 'Damien',
age: 56, //modifying worked
pets: ['Symba', 'Hades', 'Kiwi', 'Grenade'], // push worked
sibling: {
age: 45, // Modifying worked
name: 'Corentin'
}
}
// adding and deleting failed!
Notice that just like Object.freeze, Object.seal will fail silently in non-strict mode and throw a TypeError in strict mode.
Object.isSealed
We also have a method to know if an object is sealed. Quite explicit, we can call Object.isSealed to know that:
Object.isSealed(objectToSeal); // === true
let notSealedObj = { a: 54 };
Object.isSealed(notSealedObj); // === false
What about const?
You might be tempted to look at Object.seal and Object.freeze and compare them to const. Remember that they are different concepts. Object.freeze and Object.seal apply on the values of an object, and const applies on the binding. Object.freeze makes an object immutable. const creates an immutable binding. Once you assign a value to a variable, you can't assign a new value to that binding.
What about prototypes?
One last thing I need to mention: prototypes. I've writing an article about Javascript prototypes if you are not familiar with it. When it comes to Object.freeze and Object.seal, know that you also can't change their prototypes once they are frozen or sealed.
let freezeThat = {
name: 'Damien'
}
let sealThis = {
age 28
}
Object.freeze(freezeThat)
Object.seal(sealThis)
// These two lines will fail!
Object.setPrototypeOf(freezeThat, {x: 26})
Object.setPrototypeOf(sealThis, {alive: true})
setPrototypeOf is used to change the prototype of an object. When the object is sealed or frozen, you will not be able to do that. As always, in non-strict mode, it will fail silently. In strict mode, you will see a TypeError: Object is not extensible.
Conclusion
It's important to know the differences between Object.freeze and Object.seal. Being aware of those differences will avoid you some trouble when you use them in your code.
To recap: