# All about Prototypal Inheritance and classical inheritance

JavaScript is not a class-based object-oriented language. But it still has ways of using object-oriented programming (OOP). JavaScript is a **prototype-based language**.

> A prototype-based language is a type of programming language in which objects are created by cloning or inheriting from existing objects called prototypes. Instead of using classes and class hierarchies like in class-based languages, prototype-based languages directly work with objects.

## What is a prototype?

**A prototype is an object that is used as a blueprint for creating new objects.Every object in JavaScript has a prototype, which can be accessed using the** `__proto__` **property.**

In JavaScript, each object has a hidden property called `[[Prototype]]`, which references its prototype.

All JavaScript objects inherit properties and methods from a prototype.

Let's see with examples

```javascript
let arr = ["a","b"];
console.log(arr);
```

output

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684493181665/75d3e6ae-edef-415a-8cd6-fde8be2899bf.jpeg align="center")

To find the `[[Prototype]]` of an object, we will use the `Object.getPrototypeOf()` method.

```javascript
let arr = ["a","b"];
console.log(Object.getPrototypeOf(arr));
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684493239950/4f335c49-417e-422e-a8d6-456f5fb5b247.jpeg align="center")

When we create a simple array in javascript, along with its element, notice that there is one more property called `[[Prototype]]` .this is added by the javascript engine automatically.

when we expand this then all these extra built-in methods and functions reside inside a property`[[Prototype]]`. Yes, and here prototypes come into the picture.

The double square brackets that enclose `[[Prototype]]` signify that it is an internal property, and cannot be accessed directly in code.

Now lets in Objects

```javascript
let details = {
  name: "John",
  country: "India",
  getInfo : function() {
    console.log(`My name is ${this.name}. I live in ${this.country}`)
  } 
}
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684493561596/fd97ab17-d133-40ef-b78f-dff107e6d149.jpeg align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1684493581037/2d5f7a9c-fe1f-49f2-b489-74ef02440675.jpeg align="center")

JavaScript objects inherit methods and properties from one another using prototypes. Every object has its own property called a prototype. As the prototype is also an object, it has its own prototype object. This is called prototype chaining and it ends when a prototype has null for its prototype.

The `prototype` property, on the other hand, is a property of constructor functions. It is an object that serves as the prototype for objects created by that constructor function using the `new` keyword. It is used to define the shared properties and methods that will be inherited by the instances created by the constructor.

## **Prototype chaining**

The prototype chain is a mechanism that allows objects to inherit properties and methods from their prototypes, forming a chain-like structure. When you access a property or method on an object, if the object doesn't have it, JavaScript automatically looks up the property or method in its prototype (`[[Prototype]]`), and if it's not found there, it continues the search in the prototype's prototype, and so on, until the property or method is found or until the end of the prototype chain is reached.

If the property or method isn’t found anywhere in the prototype chain, only then will JavaScript return `undefined`.

for Example

**COPY**

```javascript
let arr = ["a","b"];

console.log(arr.__proto__); //Array
console.log(arr.__proto__.__proto__); // Object
console.log(arr.__proto__.__proto__.__proto__); //null

console.log(arr.__proto__ == Array.prototype); //true
console.log(arr.__proto__.__proto__ == Object.prototype); //true
console.log(arr.__proto__.__proto__.__proto__ == null); //true
```

In the above example, the Arrays prototype is Object, and Object's prototype is `null`, which indicates the end of the chain.

Let's explore the same in the case of functions:

```javascript
function func(){
    console.log("Inside function")
}

console.log(func.__proto__); //Function
console.log(func.__proto__ == Function.prototype); //true

console.log(func.__proto__.__proto__); // Object
console.log(func.__proto__.__proto__ == Object.prototype); //true

console.log(func.__proto__.__proto__.__proto__); //null
console.log(func.__proto__.__proto__.__proto__ == null); //true
```

> ***Note:- Everything in Javascript is nothing but an Object.***
> 
> `Date` **objects inherit from** `Date.prototype`
> 
> `Array` **objects inherit from** `Array.prototype`
> 
> `Person` **objects inherit from** `Person.prototype`
> 
> **The** `Object.prototype` **is on the top of the prototype inheritance chain**

Whether you make an array, or a function it is down the prototype chain ends up being an **Object**.

## **Prototypal Inheritance**

Prototypal inheritance is a mechanism in JavaScript that allows objects to inherit properties and methods from other objects. In JavaScript, objects can have a prototype, which serves as a blueprint or template for creating new objects.

When accessing a property or method on an object, JavaScript first checks if the object itself has that property or method. If it doesn't, it looks up the prototype chain to find the property or method on the object's prototype. If the prototype also doesn't have the property or method, the lookup continues up the prototype chain until it reaches the top-level object, which is usually the `Object.prototype` object.

The prototype chain is created when objects are linked together through their `[[Prototype]]` or `__proto__` internal property. By default, when creating an object using object literals (`{}`) or the `new` keyword, the object's prototype is set to `Object.prototype`. This means that all objects inherit properties and methods from `Object.prototype`

To create a prototypal inheritance relationship between objects, you can use the `Object.create()` method or constructor functions.

```javascript
// parent object
let details = {
  name: "",
  country: "",
  getInfo : function() {
    console.log(`My name is ${this.name}. I live in ${this.country}`)
  } 
}
// child object
let john=Object.create(details);
john.name="john" 
john.country="India"
john.getInfo() // My name is john. I live in India
```

We can also add a `study()` method to the `details`object.

```javascript
// parent object
let details = {
  name: "",
  country: "",
  getInfo : function() {
    console.log(`My name is ${this.name}. I live in ${this.country}`)
  } 
}
// child object
let john=Object.create(details);
john.name="john" 
john.country="India"
john.degree="BE";
details.study= function(){
  console.log(`My name is ${this.name} and studying ${this.degree}  `);
}
john.study() // My name is john and studying BE  
```

**prototypal inheritance** in JavaScript is a powerful feature that allows objects to inherit properties and methods from a parent object

Now let's talk about classical inheritance Before that we know about classes.

## Classes

In JavaScript, classes are a way to create objects that encapsulate data and behavior. They provide a structured and convenient syntax for defining reusable object blueprints.JavaScript Classes are templates for JavaScript Objects.

classes are nothing but syntactic sugars for constructor functions. They provide a new way of declaring constructor functions in javascript

### JavaScript Class Syntax

Use the keyword `class` to create a class.

Always add a method named `constructor()`

```javascript
class ClassName {
  constructor() { ... }
}
```

For Example

```javascript
class Person {
  constructor(name, grade) {
    this.name = name;
    this.grade = grade;
  }
}
```

The example above creates a class named "Person".The class has two initial properties: "name" and "grade".

When you have a class, you can use the class to create objects:

Use the keyword `class` to create a class. Always add a `constructor()` method.

Then add any number of methods.

For example

```javascript

class Person {
  constructor(name,grade) {
    this.name = name;
    this.grade = grade;
  }
   getInfo() {
    return `${this.name} got  ${this.grade} grade`;
  }
}
const student = new Person("John", "A");
const student1 = new Person("tkp", "B");
console.log(student.getInfo()); // Output: John got  A grade
console.log(student1.getInfo()); // Output: tkp got  B grade
```

## Class Inheritance

In JavaScript, class inheritance is achieved using the `extends` keyword. A class created with a class inheritance inherits all the methods from another class.

let's take another example

```javascript

class Person {
  constructor(name,grade) {
    this.name = name;
    this.grade = grade;
  }

   getInfo() {
    return `${this.name} got  ${this.grade} grade`;
  }

}
class student extends Person{
  constructor(name,grade,degree){
    super(name,grade)
    this.degree=degree;
  }
  getInfo() {
    return `${this.name} got  ${this.grade} grade in ${this.degree} degree `;
  }
}

const John = new student("John", "A","B.E");
const Tkp = new student("tkp", "B","B.sc");
console.log(John.getInfo()); // Output: John got  A grade in B.E degree 
console.log(Tkp.getInfo()); // Output: tkp got  B grade in B.sc degree
```

In the example, we have a parent class `Person` that has a `name` property and `grade` property and a `getInfo()` method. We then define a child class `student` that extends the `Person`class using the `extends` keyword. The `student`class adds a `degree` property and overrides the `getInfo()` method with its own implementation.

To call the parent class constructor from the derived class constructor, we use the `super()` method. This allows us to initialize the inherited properties of the parent class.

### Getters and Setters

In JavaScript, you can define getter and setter methods in classes using the `get` and `set` keywords. Getters and setters allow you to access and modify class properties with additional logic or validation.

```javascript
class Person {
  constructor(name) {
    this._name = name;
  }

  get name() {
    return this._name;
  }

  set name(newName) {
    if (typeof newName === "string") {
      this._name = newName;
    } else {
      console.log("Invalid name. Please provide a string value.");
    }
  }
}

class Student extends Person {
  constructor(name, grade) {
    super(name);
    this._grade = grade;
  }

  get grade() {
    return this._grade;
  }

  set grade(newGrade) {
    if (typeof newGrade === "number") {
      this._grade = newGrade;
    } else {
      console.log("Invalid grade. Please provide a numeric value.");
    }
  }
}

const student = new Student("John", 9);
console.log(student.name); // Output: "John"
console.log(student.grade); // Output: 9

student.name = "Jane";
student.grade = 10;

console.log(student.name); // Output: "Jane"
console.log(student.grade); // Output: 10

student.grade = "A"; // Output: "Invalid grade. Please provide a numeric value."
```

In this example, we have a `Person` class with a `name` property and corresponding getter and setter methods, similar to the previous example. We then create a `Student` class that extends the `Person` class.

The `Student` class introduces a new property `grade`, along with its getter and setter methods. The getter method `get grade()` returns the value of `_grade`, and the setter method `set grade(newGrade)` sets the value of `_grade` after performing a validation check.

When creating a `Student` instance, we can access and modify both the `name` and `grade` properties using the respective getter and setter methods. The getter and setter methods defined in the `Person` class are inherited by the `Student` class, allowing us to reuse the logic for validating the `name` property.
