This post was initially published on my blog. Check out the original source using the link below:
JavaScript is unique language, it doesn't use classical inheritance like languages such as Java or C++. Instead, it relies on prototypes. When i initially started learning javascript Prototype was always a confusing topic for me.
Even though with the advancement of es6, writing classes in javascript is much easier but the fact is that prototypes and inheritance are the foundation of JavaScript's object model, understanding them is essential for writing more effective and interoperable code,
In this blog, I'll break down the concepts of prototypes and inheritance in JavaScript in a friendly and easy-to-understand manner.
What are Prototypes in javascript?
In JavaScript, every object has a prototype. A prototype is another object that the original object inherits properties and methods from. We can think of it as a blueprint or a template that provides default values for the object.
Let’s take an example to make this clearer:
// Creating a simple object
const person = {
greet: function() {
console.log("Hello!");
}
};
// Creating another object that inherits from 'person'
const john = Object.create(person);
// Calling the greet method on 'john'
john.greet(); // Output: "Hello!"
In this example, john doesn't have a greet method on its own, but it can still call greet because it inherits from the person object. The greet method is part of the person prototype, and john has access to it.
How Prototypal Inheritance Works
When we try to access a property or method on an object, JavaScript first checks if that property or method exists on the object itself. If it doesn't, JavaScript then looks at the object's prototype to see if the property or method exists there. This process continues up the prototype chain until the property or method is found or until the chain ends.
Here’s another example to explain this:
const animal = {
type: 'Mammal',
sound: function() {
console.log("Generic animal sound");
}
};
const dog = Object.create(animal);
dog.bark = function() {
console.log("Woof!");
};
dog.sound(); // Output: "Generic animal sound"
dog.bark(); // Output: "Woof!"
In this example, the dog object inherits from animal via the Object.create method. Even though dog does not directly have the sound method, it can access it through its prototype (animal). This is the core idea behind prototypes and inheritance in JavaScript.
Javascript Constructor Functions and Prototypes
In JavaScript, you can use constructor functions to create objects that share the same prototype. Constructor functions are regular functions that are intended to be used with the new
keyword. When you use the new
keyword, JavaScript automatically sets the prototype of the new object to the prototype
property of the constructor function.
Let’s see how this works:
// Constructor function
function Person(name) {
this.name = name;
}
// Adding a method to the prototype
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
// Creating a new object using the constructor
const alice = new Person("Alice");
// Accessing properties and methods
console.log(alice.name); // Output: "Alice"
alice.greet(); // Output: "Hello, my name is Alice"
In this example, Person is a constructor function. When we create a new Person object with new Person("Alice"), JavaScript sets the '__proto__' of the newly created object to Person.prototype. This means alice can access the greet method even though it’s not defined directly on alice
Inheritance with Constructor Functions
You can also create more complex inheritance structures using constructor functions. Suppose you want to create a Student constructor function that inherits from Person.
Example:
// Person constructor function
function Person(name) {
this.name = name;
}
// Adding a method to the Person prototype
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name}`);
};
// Student constructor function
function Student(name, course) {
// Call the Person constructor function with 'this'
Person.call(this, name);
this.course = course;
}
// Setting up inheritance by linking prototypes
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
// Adding a method to the Student prototype
Student.prototype.study = function() {
console.log(`${this.name} is studying ${this.course}`);
};
// Creating a new Student object
const bob = new Student("Bob", "Mathematics");
// Accessing properties and methods
bob.greet(); // Output: "Hello, my name is Bob"
bob.study(); // Output: "Bob is studying Mathematics"
In this example, the Student constructor function inherits from the Person constructor function. We use Person.call(this, name) to call the Person constructor within the Student constructor, ensuring that the name property is set correctly. Then, we set up the inheritance by linking Student.prototype to Person.prototype using Object.create.
Finally, we added a study method to the Student.prototype, and the bob object can now access both greet and study methods.
Object.create vs. Constructor Functions
Both Object.create and constructor functions can be used to create objects that share behaviour, but they work differently.
Object.create: It creates a new object with the specified prototype object and properties. It's straightforward and gives more control over the prototype chain.
Constructor Functions: They initialize the object and set up the prototype chain automatically using the
new
keyword. Constructor functions are often used for creating multiple similar objects.
Understanding __proto__, prototype
__proto__: The Internal Prototype Link
proto is an internal property that every JavaScript object has. It points to the object's prototype (from which it inherits properties and methods).
When you try to access a property on an object, JavaScript first looks for the property on the object itself. If it doesn’t find it, it moves up the prototype chain by following the proto link until it either finds the property or reaches the end of the chain (null).
prototype: The Property of Constructor Functions
Only functions (specifically, constructor functions) have prototype property. It's used to define methods and properties that should be shared by all instances created by the constructor function.
When you create an object using the
new
keyword with a constructor function, the object's __proto is automatically set to the constructor's prototype.
Object.prototype: The Default Top-Level Object
- All JavaScript objects inherit from Object.prototype, which is the root of the prototype chain. This object contains basic methods like, which toString, valueOf, and hasOwnProperty etc which are defined in Object.prototype.
ES6 Classes and Prototypes
With the introduction of ES6, JavaScript now has a class
syntax, which provides a more familiar way to define constructor functions and inheritance. However, under the hood, the class syntax still uses prototypes.
Here’s the previous example rewritten using ES6 classes:
// Person class
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
// Student class that inherits from Person
class Student extends Person {
constructor(name, course) {
super(name); // Call the parent class constructor
this.course = course;
}
study() {
console.log(`${this.name} is studying ${this.course}`);
}
}
// Creating a new Student object
const charlie = new Student("Charlie", "Physics");
// Accessing properties and methods
charlie.greet(); // Output: "Hello, my name is Charlie"
charlie.study(); // Output: "Charlie is studying Physics"
Conclusion
JavaScript's prototypal inheritance might seem confusing , but once you understand the basic concepts, it becomes a important tool for creating reusable and extensible code, enabling code reuse and better code organisation.
Top comments (0)