An Introduction to Object Mutation in JavaScript
In this article, you will learn how object mutation in JavaScript works, its pitfalls, and strategies to prevent it with relevant code examples.
Join the DZone community and get the full member experience.
Join For FreeIn programming, object mutation implies that an object's state or data is mutated after creation. In other words, the operation that changes the attributes of an object in JavaScript is known as object mutation. Object mutation alters an object's values directly, making it challenging, particularly in applications where multiple operations may try to read from or write to an object simultaneously.
This article presents a discussion on object mutation in JavaScript with relevant code examples wherever necessary.
Data Types in JavaScript
Data types denote the type of data a variable or an object can hold. JavaScript supports two distinct categories of data types: primitive and user-defined or reference types.
Primitive Data Types
In JavaScript, all primitive data types are immutable by nature, i.e., you cannot alter them after they have been created. Numbers, Booleans, Strings, Bigints, Undefineds, Nulls, Symbols, and Objects are examples of primitive types.
User-Defined or Reference Data Types
User-defined data types or reference data types are objects created using primitive types or a combination of primitive and user-defined types. Typical examples of user-defined or reference types are objects and arrays.
How Variables Are Assigned and Reassigned in JavaScript
When you assign a primitive type variable to a primitive type variable, the two variables hold similar values, but they are stored in different storage locations. For example, assume that you have two variables varA
and varB
and you assign one variable to another in the following way:
var varA = 100;
var varB = varA;
console.log(varB);
When you execute the preceding piece of code, the number 100 will be displayed on the console. Now, you change the values of one of the two variables (say varB
) as shown here.
var varA = 100;
var varB = varA;
varB = 500;
console.log(varA);
Note how the value of the variable varB
has been changed to 500. When you print the value of varA
, it will still display 100. This is because these variables varA
and varB
are stored in two different memory locations. So, if you change any of them, the new or changed value will not reflect on the other variables.
What Is Object Mutation in JavaScript?
In JavaScript, the data type of an object can belong to any of the two categories: primitive or non-primitive. While primitive types are immutable, i.e., you cannot change them after creating them, you can alter non-primitive types, i.e., objects and arrays. Objects always allow their values to be changed. Hence, you can change the state of fields for a mutable type without creating a new instance.
Object mutations can create several problems, such as the following:
- Mutated objects can often lead to race conditions because of concurrency and thread-safety issues
- Mutation can introduce complexities in the source code because of predictability and thread safety issues
- Mutation can often lead to bugs that can be difficult to identify in the application's source code
- Mutation makes testing and debugging the code difficult because tracking code that leverages mutation becomes a challenge
Code Examples That Demonstrate Object Mutation
Object mutation can occur in any of the following scenarios:
- Adding, editing, or removing properties
- Using methods that can exhibit mutation
When you alter the properties of an object, either directly or indirectly, you are essentially mutating the object. The following code snippet shows how you can mutate an object by changing its property.
const author = { id: 1, name: "Joydip Kanjilal"};
author.id = 2; author.city = "Hyderabad, INDIA";
console.log(author);
In the preceding piece of code, we create an object named author that contains two properties, namely, id
and name
. While the id
property is used to store the id
of the author record, the name
property stores the name of the author. Note how we mutate the author object by altering the value pertaining to the id
property. Next, we add a new property, named city, to the author object and assign a value to the property.
When you run the preceding piece of code, the properties and their values of the author object will be displayed as shown below:
{ name: 'Joydip Kanjilal', city: 'Hyderabad, INDIA' }
When you pass an object to a function or assign it to a variable in JavaScript, you're essentially passing the reference to the object and not a copy of it. This implies that any change you make to the new object created by passing an object or assigning it to the variable will apply to all references of the actual object.
Consider the following piece of code that shows how you can create an object in JavaScript and then assign it to a variable.
const objA = { id: 1, name: 'Joydip Kanjilal',
city: 'Hyderabad, INDIA', pincode: 500089 }
const objB = objA;
objB.pincode = 500034;
console.log(objA);
In the preceding piece of code, the object objA
is assigned to objB
, and the value of the pincode property of objA
is changed, i.e., the object objA
is mutated. When you execute the program, the following data will be displayed.
{ id: 1, name: 'Joydip Kanjilal', city: 'Hyderabad, INDIA', pincode: 500034 }
Note that the value of the pincode property has been changed.
Preventing Object Mutation in JavaScript
In JavaScript, you can prevent mutation in several ways, such as the following:
- Using object cloning by taking advantage of the
Object.assign()
method or the spread operator (...) - Using the
Object.seal()
method to prevent adding or deleting properties of an object - Using the
Object.freeze()
method to prevent adding, editing, or deleting properties of an object
Using Cloning
Refer to the following piece of code that shows how you can clone an object in JavaScript using the spread operator.
let originalObj = { x: 10, y: 100 }; let clonedObj = { ...originalObj };
Here, the name of the cloned object is clonedObj
, and it is identical to the original object named originalObj
. So, if you display the values of the two properties of these two objects, the results will be the same.
Now, change the value of one of the properties of the cloned object named, clonedObj
to your desired value, as shown in the piece of code given below.
clonedObj.x = 50;
Now, write the following piece of code to display the value of the property named x
pertaining to the two objects originalObj
and clonedObj
.
console.log(originalObj.x);
console.log(clonedObj.x);
When you run the program, you'll observe that the value of the property x
in the original object is unchanged. The values will be displayed at the console as shown below:
10
50
Using the Object.freeze() Method
The Object.freeze()
method can make an object immutable by preventing any alterations to any of its properties.
const author = { id: 1, name: "Joydip Kanjilal",
city: "Hyderabad", state: "Telengana",
country: "India", pincode: 500089};
Object.freeze(author);
author.city = "Bangalore";
author.state = "Karnataka";
author.pincode = 560010;
console.log(author);
When you execute the preceding piece of code, the results will be similar to this:
{
id: 1,
name: 'Joydip Kanjilal',
city: 'Hyderabad',
state: 'Telangana',
country: 'India',
pincode: 500089
}
As you can see from the output, even if you’ve assigned values to the properties city and state, and pincode, there is no effect. So, no changes have been made to the data contained in any of the properties of the object.
Using the Object.seal() Method
You can also use the Object.seal()
method to prevent object mutation in JavaScript. This method would enable you to alter the values of existing properties, but you cannot modify or delete any of the properties of the Object. The following code example illustrates this:
const author = { id: 1, name: "Joydip Kanjilal",
city: "Hyderabad", state: "Telangana",
country: "India", pincode: 500089};
Object.seal(author);
author.city = "Bangalore";
author.state = "Karnataka";
author.pincode = 560005;
author.booksauthored = 3;
console.log(author);
In the preceding code snippet, while modifications to the properties of the object named author will be allowed, neither addition nor deletion of the object's properties will be allowed. When you run the program, you'll see that the values of the properties modified are reflected in the result, but the statements that add or delete properties are ignored. Here's how the output would look like at the console:
{
id: 1,
name: 'Joydip Kanjilal',
city: 'Bangalore',
state: 'Karnataka',
country: 'India',
pincode: 560005
}
Using the Object.defineProperty() method
You can also leverage the Object.defineProperty()
method in JavaScript to control the mutability of an object's individual properties. The following code snippet shows how you can use this method to disallow alterations to the value contained in a property whose mutability is restricted.
const author = { id: 1, name: "Joydip Kanjilal"};
Object.defineProperty(author, "booksauthored",
{
value: 3,
writable: false,
});
author.booksauthored = 5;
console.log(author.booksauthored);
When you execute the preceding piece of code, you’ll see that the number 3 is displayed on the console.
Key Takeaways
- JavaScript categorizes object types into two distinct categories: primitives (mutable) and objects (immutable).
- The term object mutation refers to the operations that alter or change an object after it has been created.
- While primitive values such as number, etc., cannot be altered, you can always change objects after they have been created.
- Since strings in JavaScript are immutable, you cannot alter them once they have been created.
- Although mutation by itself is not that bad, you should manage it carefully to reduce bugs in your applications.
- You can reduce or eliminate mutation in JavaScript by following the recommended practices and leveraging immutable data structures.
Opinions expressed by DZone contributors are their own.
Comments