OOP: Vererbung in JavaScript (Node.js): Eine ausführliche Anleitung
What is it and why? (a real-life analogy)
Imagine you decided to create your own video game. It will have different characters: elves, orcs, mages, and dragons. Each of them has common properties: name, health level, amount of experience points. They also all have common actions: run, attack, take damage.
If you were to write code for each character from scratch, you would have to repeat the same thing hundreds of times: describe the name, health, experience, write functions for running and attacking. This would be very long, boring, and, most importantly, the code would be easy to get confused in. And what if you wanted to add a new property to all characters, for example, "mana"? You would have to rewrite the code for each class!
This is where inheritance comes to the rescue. It is a mechanism that allows you to create one common class (let's call it "Character"), which describes all the common properties and methods. Then, for each specific type of character (Elf, Orc, Mage), we create our own classes that inherit everything from the "Character" class, but can also add something of their own.
Simple analogy: Imagine you have a basic pizza dough recipe. This is the "parent" recipe. Now you want to make a "Margherita" pizza and a "Pepperoni" pizza. You don't invent the dough from scratch — you take the basic recipe (inherit it) and just add your own ingredients on top. Inheritance in programming works exactly the same way.
Why is this needed in real development?
- Saves time: no need to write the same code repeatedly.
- Ease of maintenance: if you need to fix an error in common behavior, you only change the code in the parent class, and all child classes receive the fix automatically.
- Logical structure: the code becomes more structured and understandable for other developers.
Theory: how it works internally
At the heart of inheritance lies the concept of a prototype. In JavaScript (unlike classical languages like Java or C++) there are no "real" classes in the classical sense. What we call classes is syntactic sugar over prototypal inheritance.
Let's figure out what this means. Every object in JavaScript has a hidden property [[Prototype]] (which we can access via __proto__ or the Object.getPrototypeOf method). This property points to another object — the prototype.
When you try to access a property or method of an object, JavaScript first looks for it in the object itself. If it doesn't find it, it goes to the object's prototype. If it's not there either, it goes to the prototype's prototype, and so on, until it reaches null. This chain is called the prototype chain.
Important: Classes in JavaScript (introduced in ES6) are just a "wrapper" around prototypes, making the code more familiar for developers coming from Java or C++. Under the hood, prototypes are still at work.
When we write class Child extends Parent, we are telling JavaScript: "Make it so that the prototype of the Child class points to the Parent class." Now, if an object of the Child class doesn't have a certain method, JavaScript will look for it in Parent.
What it looks like in memory:
- There is a constructor function
Parent(or a class). It has a propertyprototype— this is the object that will become the prototype for all instances. - When we create
new Child(), JavaScript creates an empty object, sets its prototype (__proto__) toChild.prototype. - But
Child.prototypeitself is an object whose prototype (__proto__) points toParent.prototype. - Thus, a chain is built: instance → Child.prototype → Parent.prototype → Object.prototype → null.
The super keyword: When you override a method in a child class but also want to call the parent's version of that method, you use super. It's like saying: "Do what the parent does, and then add something of your own on top."
Syntax in JavaScript (Node.js): all forms and variations
In JavaScript, there are several ways to implement inheritance. We will look at the modern syntax (classes) and the classical one (prototypes), so you understand how it works "under the hood".
1. Modern syntax (ES6+): the class and extends keywords
This is the most common and recommended way in modern JavaScript (especially in Node.js).
// Parent class (base)
class Animal {
// The constructor is called when creating a new object
constructor(name) {
this.name = name; // name property
}
// Method that will be inherited
speak() {
console.log(`${this.name} makes a sound.`);
}
}
// Child class that inherits from Animal
class Dog extends Animal {
constructor(name, breed) {
// super() calls the parent class constructor
// This must be done before using 'this'
super(name);
this.breed = breed; // New property that Animal doesn't have
}
// Override the speak method (polymorphism)
speak() {
console.log(`${this.name} barks!`);
}
// New method that Animal doesn't have
fetch() {
console.log(`${this.name} fetches the stick.`);
}
}
// Create instances
const genericAnimal = new Animal('Creature');
genericAnimal.speak(); // Creature makes a sound.
const myDog = new Dog('Bobik', 'Dachshund');
myDog.speak(); // Bobik barks! (overridden method)
myDog.fetch(); // Bobik fetches the stick. (new method)
console.log(myDog.breed); // Dachshund