In JavaScript, object protection involves restricting the ability to modify, add, or delete properties in an object.
This can be particularly useful when you want to ensure that certain objects remain consistent throughout your application, preventing accidental or malicious alterations.
JavaScript provides several built-in methods to protect objects:
Object.preventExtensions()
Object.seal()
Object.freeze()
In this tutorial, we’ll explore each of these methods, how they work, and provide examples to illustrate their usage.
1. Object.preventExtensions()
Object.preventExtensions() prevents new properties from being added to an object. However, it allows existing properties to be modified or deleted.
Example 1: Using Object.preventExtensions()
let car = { brand: 'Toyota', model: 'Corolla' }; Object.preventExtensions(car); // Attempting to add a new property car.year = 2022; console.log(car.year); // Output: undefined (addition failed) // Modifying an existing property car.model = 'Camry'; console.log(car.model); // Output: Camry // Deleting an existing property delete car.brand; console.log(car); // Output: { model: 'Camry' }
Explanation
Object.preventExtensions(car) prevents adding new properties (car.year fails).
Modifying (car.model = ‘Camry') and deleting (delete car.brand) existing properties are still allowed.
Checking If an Object is Not Extensible
You can check if an object is non-extensible using Object.isExtensible().
console.log(Object.isExtensible(car)); // Output: false
2. Object.seal()
Object.seal() prevents adding or deleting properties to an object. However, it allows modifying the values of existing properties. In addition, it marks all existing properties as non-configurable, which means their descriptors (e.g., enumerability, configurability) cannot be changed.
Example 2: Using Object.seal()
let user = { name: 'Alice', age: 30 }; Object.seal(user); // Attempting to add a new property user.email = 'alice@example.com'; console.log(user.email); // Output: undefined (addition failed) // Modifying an existing property user.age = 31; console.log(user.age); // Output: 31 // Attempting to delete a property delete user.name; console.log(user.name); // Output: Alice (deletion failed)
Explanation
Object.seal(user) prevents adding (user.email) and deleting (delete user.name) properties.
Modifying existing properties (user.age) is still allowed.
Checking If an Object is Sealed
You can check if an object is sealed using Object.isSealed().
console.log(Object.isSealed(user)); // Output: true
3. Object.freeze()
Object.freeze() is the most restrictive of the three methods. It prevents adding, deleting, or modifying properties of an object. Additionally, it marks all properties as non-writable and non-configurable.
Example 3: Using Object.freeze()
let book = { title: 'JavaScript Essentials', author: 'John Doe' }; Object.freeze(book); // Attempting to add a new property book.year = 2023; console.log(book.year); // Output: undefined (addition failed) // Attempting to modify an existing property book.title = 'Advanced JavaScript'; console.log(book.title); // Output: JavaScript Essentials (modification failed) // Attempting to delete a property delete book.author; console.log(book.author); // Output: John Doe (deletion failed)
Explanation
Object.freeze(book) prevents adding (book.year), modifying (book.title), and deleting (delete book.author) properties.
Checking If an Object is Frozen
You can check if an object is frozen using Object.isFrozen().
console.log(Object.isFrozen(book)); // Output: true
4. Property Attributes and Protection Methods
When using Object.preventExtensions()
, Object.seal()
, and Object.freeze()
, it's helpful to understand how they interact with property attributes like writable and configurable.
Summary of Protection Methods
Method | Add Properties | Delete Properties | Modify Properties | Change Property Attributes |
---|---|---|---|---|
Object.preventExtensions |
❌ No | ✅ Yes | ✅ Yes | ✅ Yes |
Object.seal |
❌ No | ❌ No | ✅ Yes | ❌ No |
Object.freeze |
❌ No | ❌ No | ❌ No | ❌ No |
5. Protecting Nested Objects
Object.freeze() and Object.seal() do not perform deep protection. They only apply to the object at the top level, not nested objects.
Example 4: Protecting Nested Objects
let user = { name: 'Alice', address: { city: 'New York', zip: '10001' } }; Object.freeze(user); // Attempting to modify a nested object property user.address.city = 'Los Angeles'; console.log(user.address.city); // Output: Los Angeles (modification succeeded)
Explanation
Even though Object.freeze(user) freezes the user object, the nested address object remains mutable.
To deeply freeze an object, you need to create a utility function.
Example 5: Deep Freezing an Object
function deepFreeze(obj) { // Retrieve the property names defined on obj let propNames = Object.getOwnPropertyNames(obj); // Freeze properties before freezing self for (let name of propNames) { let value = obj[name]; // If value is an object, freeze it if (value && typeof value === 'object') { deepFreeze(value); } } return Object.freeze(obj); } let user = { name: 'Alice', address: { city: 'New York', zip: '10001' } }; deepFreeze(user); // Attempting to modify a nested object property user.address.city = 'Los Angeles'; console.log(user.address.city); // Output: New York (modification failed)
Explanation
The deepFreeze() function recursively freezes the properties of an object, including nested objects.
After using deepFreeze(), attempts to modify nested properties are also prevented.
6. Working with Property Descriptors
You can use Object.defineProperty() to set property attributes like writable, enumerable, and configurable, providing more control over how individual properties can be accessed or modified.
Example 6: Using Object.defineProperty() for Property Protection
let user = {}; Object.defineProperty(user, 'name', { value: 'Alice', writable: false, // Cannot be modified enumerable: true, configurable: false // Cannot be deleted }); console.log(user.name); // Output: Alice user.name = 'Bob'; // Attempt to modify (fails silently) console.log(user.name); // Output: Alice delete user.name; // Attempt to delete (fails silently) console.log(user.name); // Output: Alice
Explanation
writable: false makes the property read-only.
configurable: false makes the property non-deletable and prevents its descriptor from being modified.
Summary
Object protection in JavaScript helps to control how and if an object's properties can be modified, added, or deleted. Here is a quick overview of the methods:
Methods for Object Protection
Method | Description |
---|---|
Object.preventExtensions() |
Prevents new properties from being added to the object. |
Object.seal() |
Prevents adding or deleting properties; allows modification of existing properties. |
Object.freeze() |
Prevents adding, deleting, or modifying properties. |
Additional Tips
- Use
Object.isExtensible()
,Object.isSealed()
, andObject.isFrozen()
to check the state of an object. - For deeply nested objects, use a custom function (e.g.,
deepFreeze()
) to apply protection to all levels. - Utilize
Object.defineProperty()
to set property attributes (writable
,enumerable
,configurable
) for more granular control.
By understanding and using these methods, you can ensure that your objects are safeguarded against unwanted changes, making your JavaScript applications more robust and reliable.