Breaking down reflection, enumeration and deletion - 3 critical pieces of working with data in objects.
As we saw in the previous OOP article, we can use the Object.create() method to create a new object which links up to another object, and when we have this parent-child relationship, the parent object is called the child's Prototype.
In the example below, myPerson would be considered the Prototype of chris.
// create a parent object
const myPerson = {
arms: 2,
legs: 2,
};
// create a child object that links to parent object
const chris = Object.create(myPerson);
If you want to know whether an object has a property, we can look it up using either dot or bracket notation. What if we were to look up a property that does not exist on the object? For example, chris.hands would yield the value undefined. But what if we want to look up a property that exists further up the prototypal chain?
// create a parent object
const myPerson = {
arms: 2,
legs: 2,
};
// create a child object that links to parent object
const chris = Object.create(myPerson);
console.log(chris.hands);
// undefined
If we try and reference chris.arms the JavaScript interpreter will first look on the chris object, see that it's not there, then attempt to look further up the prototypal chain, up to myPerson, and there it will see that myPerson does indeed have the property arms, and it will return the corresponding value, 2.
So using dot or bracket notation to lookup a property doesn't necessarily guarantee that that that property is on the object itself - it might be on that object's prototype. So how can we be sure that a property exists on the object itself?
One way to accomplish this is by using the hasOwnProperty method. Let's give this a try:
// create a parent object
const myPerson = {
arms: 2,
legs: 2,
};
// create a child object that links to parent object
const chris = Object.create(myPerson);
console.log(chris.hasOwnProperty('arms'));
// false
We can see from the above example that no, the object chris itself does not have the property arms.
Another way to check if an object has a property: we can use the for..in loop to loop over the properties of an object, for example:
for (let key in chris) {
console.log(key);
}
// arms
// legs
The issue with this is that when I console.log() each key, the for..in loop will not only loop over the keys of chris itself, but it will also loop over any keys I have created on that object's prototype. So, we can combine a for..in loop with the hasOwnProperty method:
for (let key in chris) {
console.log(key, chris.hasOwnProperty(key));
}
// arms false
// legs false
In the above example we can use hasOwnProperty to filter out the properties which we don't care about. One thing to bear in mind during this process is that the order of keys in an object is not necessarily guaranteed. So if you need a solution that ensures the keys are always in order, you can reach for a different data structure like a Map, or make sure your object is always sorted.
You can also use an array, and if you're using some sort of method to loop over the array, you don't need to worry about properties that exist up the prototypal chain, but there might be some performance drawbacks to using an array over an object. You can learn more about this by studying Big O Notation, specifically around constant time lookup of Objects, and linear time lookup of Array elements.
If we want to delete a property from an object, for example, if we have a property on chris:
const chris = Object.create(myPerson);
chris.hands = 2;
console.log(chris);
// { hands: 2 }
What if we want to delete this property from chris? We can use the delete keyword:
const chris = Object.create(myPerson);
chris.hands = 2;
console.log(chris);
// { hands: 2 }
delete chris.hands;
console.log(chris);
// {}
However, what would happen if we try to delete a property that exists further up the prototypal chain?
const chris = Object.create(myPerson);
chris.hands = 2;
console.log(chris);
// { hands: 2 }
delete chris.hands;
console.log(chris);
// {}
delete chris.arms;
console.log(chris.arms);
// 2
Does chris still have the property arms - which exists up the chain? Yes, it still does. So I was not able to delete a property up the chain using delete. So in order to delete that property arms, I would need to either remove the link between chris and myPerson, or delete arms directly from myPerson. When we have properties that exist up the chain which cannot be deleted, they are called non-configurable properties.
Any properties that exist on objects by default, such as Object.hasOwnProperty or Array.push, are considered non-configurable properties. They exist by default - they are there natively - and they cannot be deleted.