1console.log("hello-world);
console.log("hello-world);
.container {
width: 80%;
}
<pre><code class="language-css">
.container {
width: 80%;
}
</code></pre>
1console.log("hello-world);
console.log("hello-world);
.container {
width: 80%;
}
<pre><code class="language-css">
.container {
width: 80%;
}
</code></pre>
Today we dive deep into the art of clearing objects in JavaScript. This comprehensive guide will cover different property types (inherited, enumerable), various techniques for effectively resetting or clearing JavaScript objects, including simple objects, nested objects, and those with non-enumerable properties, and immutability. We’ll also address common pitfalls, best practices and a nice Practice Question to cement the concepts.
JavaScript objects have properties that can be categorized as either own properties or inherited properties. Furthermore, these properties can be either enumerable or non-enumerable.
for...in
loops. Simple assignment using dot notation (e.g., obj.property = value
) or square bracket notation (e.g., obj['property'] = value
) creates enumerable properties.for...in
loops. They are typically defined using Object.defineProperty()
with enumerable: false
.function User(name) {
this.name = name; // Own property
}
User.prototype.greet = function() { // Inherited property
return `Hello, ${this.name}!`;
};
const user1 = new User('Alice');
console.log(user1.hasOwnProperty('name')); // true (own property)
console.log(user1.hasOwnProperty('greet')); // false (inherited property)
const user = { name: 'Bob' }; // Enumerable property
Object.defineProperty(user, 'age', {
value: 30,
enumerable: false // Non-enumerable property
});
for (const key in user) {
console.log(key); // Outputs: name
}
console.log(Object.getOwnPropertyNames(user)); // Outputs: ['name', 'age']
for...in
Loop, hasOwnProperty
and delete
A straightforward method to clear an object is using a for...in
loop along with the delete
operator. This method is effective for objects with enumerable properties.
const userSettings = { theme: 'dark', notifications: true };
for (const key in userSettings) {
if (userSettings.hasOwnProperty(key)) {
delete userSettings[key];
}
}
console.log(userSettings); // Outputs: {}
hasOwnProperty
.delete
operator.This method uses Object.keys()
to retrieve an array of an object’s own enumerable property names, then iterates over them with forEach()
. Alternatively, a for...of
loop can be used in combination with Object.keys()
.
// forEach example:
const sessionData = { token: 'abc123', expires: '2023-01-01' };
Object.keys(sessionData).forEach(key => {
delete sessionData[key];
});
console.log(sessionData); // Outputs: {}
// - - - - - - - - - - - - - -
// for...of example:
const appConfig = { mode: 'development', debug: true };
for (const key of Object.keys(appConfig)) {
delete appConfig[key];
}
console.log(appConfig); // Outputs: {}
Sometimes you may need to clear only a nested object within a larger object. The same approach with a for...in
loop can be applied. And as a best practice, do check if its the object’s own property.
const userProfile = {
name: 'Alice',
details: { age: 30, location: 'Wonderland' }
};
for (const key in userProfile.details) {
if (userProfile.details.hasOwnProperty(key)) {
delete userProfile.details[key];
}
}
console.log(userProfile); // Outputs: { name: 'Alice', details: {} }
Reassigning an object to an empty object {}
is a quick way to clear it. Suitable way :
let
or var
. Note that reassigning will not affect other references to the original object. This is important in scenarios where the object is shared or referenced in multiple places.let
(or var
) to an empty object is more efficient. This method is quick and lets the JavaScript engine handle the previous object’s garbage collection.let settings = { theme: 'light', volume: 70 };
settings = {};
console.log(settings); // Outputs: {}
Object.getOwnPropertyNames
: Clear Objects with Non-Enumerable PropertiesFor objects that include non-enumerable properties, Object.getOwnPropertyNames
combined with a loop or forEach
can be used.
let config = { display: 'bright' };
Object.defineProperty(config, 'volume', {
value: 'high',
enumerable: false
});
Object.getOwnPropertyNames(config).forEach(prop => {
delete config[prop];
});
console.log(config); // Outputs: {}
obj = {}
creates a new object and assigns it to obj
. However, this does not affect other variables that reference the original object. These variables will still point to the original object, which now remains unchanged.for...in
, only iterate over enumerable properties. Non-enumerable properties, which are not listed in Object.keys()
or for...in
loops, need Object.getOwnPropertyNames
or Object.getOwnPropertyDescriptors
for their enumeration.delete
: The delete
operator can be less performant, especially in browsers and with large objects. It modifies the object’s shape (how properties are stored), leading to potential optimization issues. Reassigning an object to a new empty object is generally more performant as it doesn’t involve property-by-property deletion.for...in
loop: When using the for...in
loop, it’s crucial to use hasOwnProperty
to check if a property belongs to the object itself and is not inherited from the prototype chain. This prevents inadvertently iterating over inherited properties, which might not be the intended behavior. See detailed explanation below.Immutable data patterns involve creating new objects whenever modifications are necessary, rather than changing the existing objects. This approach helps in maintaining the original state and avoiding side effects, especially in scenarios involving shared references.
Consider a scenario where you have a user object and you want to update the user’s address. Instead of directly modifying the original user
object, we’ll create a new object with the updated address.
const user = {
name: 'Alice',
address: '123 Main St'
};
function updateUserAddress(user, newAddress) {
return {
...user,
address: newAddress
};
}
const updatedUser = updateUserAddress(user, '456 Elm St');
console.log(user); // Outputs: { name: 'Alice', address: '123 Main St' }
console.log(updatedUser); // Outputs: { name: 'Alice', address: '456 Elm St' }
In this example, updateUserAddress
returns a new object created using the spread syntax (...
). This new object contains all the properties of user
but with the address
property updated. The original user
object remains unchanged, demonstrating the principle of immutability.
for...in
LoopWhen iterating over an object’s properties using a for...in
loop, it’s important to distinguish between the object’s own properties and those it inherits from its prototype.
Let’s illustrate this with an object that inherits from another object through its prototype.
function Person() {
this.name = 'Alice';
}
Person.prototype.greet = function() {
return `Hello, ${this.name}`;
};
const person = new Person();
for (const key in person) {
if (person.hasOwnProperty(key)) {
console.log(key); // Outputs: name
}
}
In this example, person
has its own property name
and inherits the greet
method from its prototype. The for...in
loop iterates over both properties. However, using hasOwnProperty
inside the loop ensures that only the object’s own properties (name
in this case) are considered, and the inherited greet
method is ignored.
Above approach avoids unexpected behavior when dealing with inherited object properties, ensuring that only the properties belonging directly to the object are processed.
In the spirit of Test Driven Development ( 😁), lets test our understanding by solving a problem.
Imagine you’re building a time capsule in JavaScript. You can add various items to it, but due to a quirky time-space limitation, you can only take items out by completely clearing the capsule and starting over.
Your task is to write two functions for the Time Capsule:
addToCapsule
: Adds an item to the time capsule.clearCapsule
: Clears all items from the time capsule.However, here’s the fun part: The time capsule should be an immutable object!
Problem
/**
* Adds an item to the time capsule.
*
* @param {Object} capsule - The current time capsule object.
* @param {String} item - The item to add to the time capsule.
* @return {Object} A new time capsule object with the added item.
*/
function addToCapsule(capsule, item) {
// > > > 👉 Write code here 👈 < < <
}
/**
* Clears all items from the time capsule.
*
* @param {Object} capsule - The time capsule object to clear.
* @return {Object} A new, empty time capsule object.
*/
function clearCapsule(capsule) {
// > > > 👉 Write code here 👈 < < <
}
// ▶ Driver Code:
let myCapsule = {};
myCapsule = addToCapsule(myCapsule, 'Dinosaur Toy');
myCapsule = addToCapsule(myCapsule, 'Ancient Coin');
console.log(myCapsule);
// Outputs: { 'Dinosaur Toy': true, 'Ancient Coin': true }
myCapsule = clearCapsule(myCapsule);
console.log(myCapsule);
// Outputs: {}
Please attempt before seeing the Answer:
function addToCapsule(capsule, item) {
return { ...capsule, [item]: true };
}
function clearCapsule(capsule) {
return {};
}
Explanation:
addToCapsule
function creates a new object each time it’s called, adding a new item to the time capsule. It uses the spread syntax to copy properties from the old capsule object and adds the new item.clearCapsule
function simply returns a new, empty object, effectively clearing the time capsule.Hopefully, this blogpost gives you enough approaches to keep all your javascript objects tidy, and avoid edge cases due to different property types (non-enumerable, inherited props, etc) and even immutability.
Keep clean coding! 🚀👨💻🧹
Feel free to reach out!