Deep Dive into JavaScript Objects: Property Descriptors, Object.create(), and Iteration
In this blog, we’ll dive deep into JavaScript objects, exploring various techniques and methods for managing properties, creating objects, and efficiently iterating over them.
Understanding Property Descriptors
Let's start with an example you're likely familiar with: Math.PI
. We know that Math.PI
holds the value 3.141592653589793
, and you can’t change it, even if you try:
console.log(Math.PI);
Math.PI = 5;
console.log(Math.PI); // still 3.141592653589793
Why can't we change the value of Math.PI
? To understand this, we can inspect the property descriptor using Object.getOwnPropertyDescriptor()
:
const descriptor = Object.getOwnPropertyDescriptor(Math, "PI");
console.log(descriptor);
//outputs:
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
We can see that writable
, enumerable
, and configurable
are all set to false
. This means the value is non-writable (cannot be changed), non-enumerable (won’t show up in loops), and non-configurable (cannot be deleted or redefined).
Declaring Objects Using Object.create()
Next, let's see a different way of creating objects using Object.create()
. It allows us to create a new object that inherits from an existing one:
const obj = {
name: "harsh",
age: 21
}
const mynewObj = Object.create(obj);
console.log(mynewObj); // outputs: {}
Reason for {} : When you use Object.create(obj), the new object (mynewObj) doesn't have its own properties, but it inherits properties from obj via its prototype. This means that mynewObj can access the properties of obj (like name and age), but these properties don't exist directly on mynewObj itself—they exist in its prototype.
console.log(mynewObj.name); // "harsh"
console.log(mynewObj.age); // 21
Modifying Object Properties with Object.defineProperty()
You can modify or define properties in an object using Object.defineProperty()
. This allows you to control attributes like writable
, enumerable
, and configurable
.
const chai = {
name: "gingerChai",
price: 250,
isAvailable: true
};
console.log(chai);
//outputs: {name: 'gingerChai', price: 250, isAvailable: true}
Now does it have descriptor properties? and if yes then how can I set its descriptor properties, So first look that those properties are available to us or not.
console.log(Object.getOwnPropertyDescriptor(chai));
//undefined
Reason for this undefined: We are using Property Descriptor and we have only passed object name but we haven’t passed the property name, let’s pass that too.
console.log(Object.getOwnPropertyDescriptor(chai, "name"));
//outputs:
{
value: 'gingerChai',
writeable: true,
enumerable: true,
configurable: true
}
Now this is our code and we have written this, so javascript gives us some control hat we can change it’s properties, but this control is limited only to some extent. so we can’t do something wrong by changing any property.
It takes 3 parameters:
Object name
The property that we want to disrupt
define the properties inside the object
const chai = {
name: "gingerChai",
price: 250,
isAvailable: true
};
Object.defineProperty(chai, "name", {
writable: false,
enumerable: false
});
console.log(Object.getOwnPropertyDescriptor(chai, "name"));
//outputs:
{
"value": "gingerChai",
"writable": false,
"enumerable": false,
"configurable": true
}
Now, if you try to change the name
property or iterate over it, you’ll notice the following:
The
name
property is non-enumerable, meaning it won’t show up in loops.The
name
property is non-writable, meaning you can't change its value.
Iterating Over Object Properties
If we want to loop through the object keys and values, we can use for...of
combined with Object.entries()
, first let try to loop over it without Object.entries()
:
for (let [key, value] of chai) {
console.log(`${key}: ${value}`);
}
//outputs: throws error (chai is not iterable)
Object is iterable or not depends on different situation, now to iterate over this object we can use Object.entries()
:
for (let [key, value] of Object.entries(chai)) {
console.log(`${key}: ${value}`);
}
//outputs:
price: 250
isAvailable: true
Since we set enumerable: false
for the name
property, it’s no longer included in the iteration.
Adding Functions to Objects
You can also add methods (functions) to objects, like this:
const chai = {
name: "gingerChai",
price: 250,
isAvailable: true,
orderChai: function() {
console.log("Chai is not ready");
}
};
for (let [key, value] of Object.entries(chai)) {
console.log(`${key}: ${value}`);
}
price: 250
isAvailable: true
orderChai: function(){
console.log("chai is not ready");
}
we can see we are getting the function directly and sometimes we want to skip some iterations in object or array, so we can add a if condition to skip this function iteration
for (let [key, value] of Object.entries(chai)) {
if (typeof value !== "function") {
console.log(`${key}: ${value}`);
}
}
//outputs:
price: 250
isAvailable: true
Wrapping Up
In this blog, we explored several key concepts related to JavaScript objects, including:
Viewing and manipulating property descriptors.
Creating objects with
Object.create()
and understanding prototype inheritance.Modifying object properties with
Object.defineProperty()
.Iterating over objects and filtering out certain properties.
By mastering these techniques, you can work more effectively with JavaScript objects and have better control over how properties behave.