Freezing or Sealing? Understanding JavaScript Object Immutability

by Pinta

2 min read

JavaScript, like many dynamic languages, offers flexibility in how you handle objects. But sometimes, you need to ensure certain objects remain unchanged. That's where Object.freeze() and Object.seal() come into play. They're your tools for creating immutable or partially immutable objects, respectively. Let's dive in!

In programming, immutability means an object's state cannot be changed after it's created. This can help prevent accidental modifications and make your code more predictable. JavaScript provides a few ways to achieve this, with freeze() and seal() being two prominent methods.

Object.freeze(): Preventing Object Modifications

Object.freeze() takes your object and encases it in an unbreakable shell. Here's what happens:

  1. Non-extensible: You cannot add new properties to the object.
  2. Non-configurable: Existing properties cannot be deleted or have their attributes (configurable, writable, enumerable) changed.
  3. Non-writable: Values of existing properties cannot be modified.

Example: Frozen Settings

const appConfig = Object.freeze({
    theme: "dark",
    version: "1.0.0",
    maxItems: 100,
});

appConfig.theme = "light"; // Fails silently
appConfig.language = "en"; // Fails silently
delete appConfig.version; // Fails silently

Here, appConfig is like a locked-down configuration file. Even trying to change its properties directly has no effect.

Object.seal(): Preventing Property Addition and Deletion

Object.seal() offers a slightly less restrictive form of immutability. Here's how it differs from freeze():

  1. Non-extensible: Just like freeze(), you cannot add new properties.
  2. Non-configurable: Existing properties cannot be deleted or have their attributes changed.
  3. Writable: The values of existing properties can still be modified.

Example: Modifiable Data Record

const userProfile = Object.seal({
    name: "Alice",
    email: "alice@example.com",
    isAdmin: false,
});

userProfile.email = "newalice@example.com"; // Succeeds
userProfile.age = 30; // Fails silently
delete userProfile.isAdmin; // Fails silently

Here, userProfile is like a sealed record where you can update the information but not add new fields or remove existing ones.

How to Choose: freeze() or seal()?

  • Object.freeze(): Use when you need absolute immutability for values that should never change under any circumstances. Think of configuration settings, mathematical constants, or any data you want to protect from accidental modifications.

  • Object.seal(): Use when you want a fixed structure for your object but need the flexibility to update the values of existing properties. This is suitable for data records, entities, or objects where the shape is fixed, but the data it holds might evolve over time.

Important Considerations:

  • Shallow vs. Deep Immutability: Both freeze() and seal() provide shallow immutability. If your object has nested objects or arrays, those nested structures are not automatically made immutable. You'd need to recursively apply freeze() or seal() to achieve deep immutability.

  • Object.isFrozen() and Object.isSealed(): You can use these functions to check if an object has been frozen or sealed.

Object.isFrozen(appConfig); // true
Object.isSealed(userProfile); // true

In Conclusion

Immutability is a powerful concept in JavaScript, and Object.freeze() and Object.seal() are valuable tools to help you enforce it. Choose the right one based on your specific needs, and remember to handle nested structures for deep immutability. By embracing immutability, you can make your code more robust, predictable, and easier to reason about.