In JavaScript, objects can inherit properties and methods from another object, creating a prototype chain. This means when a property is accessed on an object, JavaScript first looks for it on the object itself (known as own properties). If not found, it checks the object's prototype and continues up the prototype chain until the property is found or the chain ends at null.
const obj = {};
// Add a key named "toString" to the object.
obj.toString = function () {
return "Custom toString method";
};
// Attempt to call the custom `toString` method.
console.log(obj.toString()); // Output: "Custom toString method"
// Delete the custom `toString` property.
delete obj.toString;
// Call the default `toString` method from Object.prototype.
console.log(obj.toString()); // Output: "[object Object]"
Output
Custom toString method [object Object]
Prototype and Prototype Chain
Every object in JavaScript has an internal link called [[Prototype]], which refers to its prototype object. The prototype can also have its own prototype, creating a chain known as the prototype chain.
const pObj = { a: 1, b: 2 }; // Parent object
const cObj = Object.create(pObj); // Create a child with parent as prototype
cObj.c = 3; // Own property
console.log(cObj.a); // inherited from parent
console.log(cObj.c); // own property
console.log(cObj.d); // not found in the chain
Output
1 3 undefined
- cObj.a: Found in the parent object.
- cObj.c: Found as an own property.
- cObj.d: Not found in the object or its prototype chain.
How Inherited Properties Work
When accessing a property on an object
- JavaScript first checks the object itself for the property.
- If not found, it checks the prototype object.
- This process continues up the prototype chain until
- The property is found, or
- The chain ends at null.
const obj = { x: 10 };
const proto = { y: 20 };
Object.setPrototypeOf(obj, proto); // Set proto as the prototype of obj
console.log(obj.x); //own property
console.log(obj.y); //inherited
Output
10 20
Property Shadowing
If an object has its own property with the same name as an inherited property, the own property takes precedence. This is called property shadowing.
const pObj = { value: 42 };
const cObj = Object.create(pObj);
cObj.value = 10; // Shadows the inherited property
console.log(cObj.value); //own property takes precedence
console.log(pObj.value); //unchanged
Output
10 42
Dynamic Nature of Inheritance
JavaScript's prototype chain is dynamic, meaning you can modify the prototype of an object after it has been created, and the changes are reflected in all objects inheriting from it.
const pObj = {};
const cObj = Object.create(pObj);
pObj.newProp = "Added later";
console.log(cObj.newProp); // "Added later" (dynamic inheritance)
Output
Added later
Checking for Inherited Properties
To determine whether a property is an own property or an inherited property, you can use:
- hasOwnProperty(): Checks if the property exists directly on the object.
- Object.hasOwn(): A newer and safer method to check for own properties.
const pObj = { inherited: true };
const obj = Object.create(pObj);
obj.own = true;
console.log(obj.hasOwnProperty("own"));
console.log(obj.hasOwnProperty("inherited"));
console.log(Object.hasOwn(obj, "own"));
console.log(Object.hasOwn(obj, "inherited"));
Output
true false true false
Using Prototypes for Methods
Inherited properties can also include methods, allowing you to define reusable functionality across objects.
const parent = {
greet() {
return `Hello, ${this.name}`;
},
};
const child = Object.create(parent);
child.name = "Parent";
console.log(child.greet());
Output
Hello, Parent
End of the Prototype Chain
The prototype chain ends when a prototype is null. For example, Object.prototype is the default prototype for all objects and has null as its prototype.
const obj = Object.create(null); // Object with no prototype
console.log(Object.getPrototypeOf(obj));
console.log(obj.toString); // not inherited
Output
null undefined
Practical Use Case
Object inheritance is commonly used in JavaScript to create hierarchies, where shared functionality is defined on a prototype and inherited by multiple objects.
function Animal(name) {
this.name = name;
}
Animal.prototype.sound = function () {
return `${this.name} makes a sound.`;
};
const dog = new Animal("Dog");
console.log(dog.sound());
const cat = new Animal("Cat");
console.log(cat.sound());
Output
Dog makes a sound. Cat makes a sound.
Performance Considerations
Accessing properties high in the prototype chain or properties that don’t exist can impact performance.
Use hasOwnProperty() to avoid iterating over inherited properties when needed.
const parent = { prop: "inherited" };
const obj = Object.create(parent);
obj.ownProp = "own";
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// Logs only own properties
console.log(`${key}: ${obj[key]}`);
}
}
Output
ownProp: own