JavaScript has a different inheritance mechanism than most conventional OOP languages. Prototypes are the main focus, whereas ES6 classes offer a more contemporary method. Let's examine how ES6 classes improve readability and usefulness as well as how prototype inheritance operates.
1. Prototype: The Foundation of Inheritance
Every object in JavaScript has an internal link to another object called its prototype. This prototype object can have its own prototype, forming a chain.
Example:
const animal = { eats: true };
const rabbit = Object.create(animal);
rabbit.hops = true;
console.log(rabbit.eats); // true (inherited)
console.log(rabbit.hops); // true (own property)
Explanation:
Here, rabbit
inherits eats
from animal
. This demonstrates how objects can share properties through inheritance.
2. Constructor Functions: Building Objects
Before ES6 classes, JavaScript used constructor functions to create objects and initialize their properties.
Example:
function Animal(name) {
this.name = name;
}
Animal.prototype.eats = true;
const dog = new Animal('Dog');
console.log(dog.name); // Dog
console.log(dog.eats); // true
Explanation:
The Animal
constructor initializes name
. The eats
property is added through the Animal.prototype
, enabling inheritance.
3. Master Object: The Common Ancestor
A master object serves as the prototype for other objects.
Example:
const masterObject = { type: 'Generic' };
const specificObject = Object.create(masterObject);
specificObject.name = 'Specific';
console.log(specificObject.type); // Generic (inherited)
console.log(specificObject.name); // Specific (own property)
Explanation:masterObject
is the common ancestor, and specificObject
inherits its type
property while adding name
.
4. Prototype Chain: Following the Hierarchy
JavaScript looks up the prototype chain to find properties and methods.
Example:
const grandparent = { role: 'grandparent' };
const parent = Object.create(grandparent);
parent.role = 'parent';
const child = Object.create(parent);
console.log(child.role); // parent
Explanation:
The child
object looks for role
. It finds parent
's role
, demonstrating how the prototype chain resolves property lookups.
5. Prototype Inheritance: Sharing Methods
Objects can share methods through prototype inheritance.
Example:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + ' makes a noise.');
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(this.name + ' barks.');
};
const dog = new Dog('Rex');
dog.speak(); // Rex makes a noise.
dog.bark(); // Rex barks.
Explanation:Dog
inherits from Animal
, allowing it to access speak
. It also defines its own bark
method.
6. ES6 Classes: A Cleaner Syntax
ES6 introduced a cleaner, more intuitive way to create classes.
Example:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
Explanation:
This class-based syntax simplifies the creation and inheritance of objects, making the code more readable.
7. Getters and Setters: Managing Properties
ES6 allows defining methods to access or modify object properties dynamically.
Example:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() {
return this.width * this.height;
}
set area(value) {
this.width = Math.sqrt(value);
this.height = Math.sqrt(value);
}
}
const rect = new Rectangle(10, 20);
console.log(rect.area); // 200
rect.area = 100;
console.log(rect.width); // 10
console.log(rect.height); // 10
Explanation:area
is a computed property using a getter and setter, allowing dynamic updates.
8. Static Methods: Utility on the Class Level
Static methods belong to the class itself and not to instances.
Example:
class MathHelper {
static add(a, b) {
return a + b;
}
}
console.log(MathHelper.add(2, 3)); // 5
Explanation:add
is a static method accessible directly on MathHelper
, useful for utility functions.
9. Polymorphism: Overriding Methods
Polymorphism allows subclasses to redefine methods from the parent class.
Example:
class Animal {
speak() {
console.log('Animal makes a noise.');
}
}
class Dog extends Animal {
speak() {
console.log('Dog barks.');
}
}
const myPet = new Dog();
myPet.speak(); // Dog barks.
Explanation:Dog
overrides speak
from Animal
, providing its own implementation.
Conclusion
The foundation of JavaScript object-oriented programming is made up of ES6 classes and prototype inheritance. Writing reusable, maintainable code is improved by knowing how to use constructor functions, prototypes, and ES6 classes. To fully utilize JavaScript's inheritance paradigm, embrace these ideas!