What is Prototype Inheritance in JavaScript?

Overview

Prototype inheritance in javascript is the linking of prototypes of a parent object to a child object to share and utilize the properties of a parent class using a child class.

Prototypes are hidden objects that are used to share the properties and methods of a parent class with child classes.

Syntax

The syntax used for prototype inheritance has the proto property which is used to access the prototype of the child. The syntax to perform a prototype inheritance is as follows :

child.__proto__ = parent;        

Example

// Creating a parent object as a prototype
const parent = {
  greet: function() {
    console.log(`Hello from the parent`);
  }
};

// Creating a child object
const child = {
  name: 'Child Object'
};

// Performing prototype inheritance
child.__proto__ = parent;

// Accessing the method from the parent prototype
child.greet(); // Outputs: Hello from the parent
        

In this example, parent is the parent object acting as the prototype, and child is the child object that inherits from the parent prototype using the __proto__ property. This allows the child object to access the greet method defined in the parent object directly.

While the __proto__ property can be used to perform prototype inheritance, it's important to note that directly manipulating __proto__ is not recommended in production code. Instead, the Object.create method or constructor functions with prototype are typically used for setting up prototype chains in a safer and more maintainable way.

Cons Of Prototypical Inheritance

  • Shared References: Objects created from the same prototype share properties and methods, which can lead to unintended changes in behavior if one instance modifies a property or method.
  • Lack of Privileged Members: Privileged members, which are private members accessible to inherited objects, are not directly achievable with prototypical inheritance.
  • Complexity in Debugging: Debugging can be challenging in deeply nested prototype chains, especially when changes to the prototype affect multiple objects.
  • Limited Control over Inheritance: Inheritance through the prototype chain can sometimes be less intuitive and less flexible compared to class-based inheritance, making it difficult to control the inheritance hierarchy precisely.
  • Performance Implications: Accessing properties deep in the prototype chain can be slower than accessing properties on a local object, affecting performance in certain scenarios.
  • It reduces flexibility as a single proto property can inherit only one class.
  • Multiple inheritances can only be achieved at a different level. If we want to inherit a second class we need to use the proto.__proto__ property which makes the hierarchy complex and difficult to keep track of.
  • Only objects are available for making prototype relations.
  • Accessing elements with the same name as an element in the base class is difficult, that is you can have properties in different classes with the same name, but accessing them is difficult.
  • Same prototypes can't be inherited as this can form a loop.Properties And Methods

Properties And Methods

Properties:

  • Properties are the characteristics associated with an object, such as its state or attributes.
  • In the context of prototypical inheritance, properties defined in the prototype are shared among all instances that inherit from that prototype.
  • When a property is accessed on an object, JavaScript searches for it in the object itself and then in its prototype chain.
  • Properties can be data values, arrays, objects, or any other JavaScript data type.
  • Properties can be data values, arrays, objects, or any other JavaScript data type.

// Example of properties in a prototype object
const animal = {
  type: "Unknown",
  sound: "Undefined"
};        

Methods:

  • Methods are functions that are associated with an object and can perform actions or computations on the object's data.
  • In prototypical inheritance, methods defined in the prototype can be shared among all instances that inherit from that prototype.
  • Methods can be called on an object, and they have access to the object's properties and other methods.
  • Methods can perform various operations, such as manipulating data, performing calculations, or interacting with other objects or the environment.

// Example of a method in a prototype object
const animal = {
  makeSound: function() {
    console.log(`The ${this.type} makes a ${this.sound} sound.`);
  }
};        

Object.create

Object.create is a method in JavaScript that creates a new object with the specified prototype object and properties. It allows you to create an object that inherits from a prototype without the need to define a constructor function. This method is commonly used to set up the prototype chain for objects, enabling prototype-based inheritance.

The syntax for Object.create is as follows:

Object.create(proto, [propertiesObject]);        

- proto: This parameter is the prototype object that the newly created object will inherit from.

- propertiesObject (optional): This parameter is an object that specifies additional properties for the newly created object. These properties are added to the newly created object and override properties with the same name in the prototype chain.

Here's an example demonstrating how to use Object.create:

// Creating a prototype object
const personPrototype = {
  introduce: function() {
    console.log(`Hi, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

// Creating a new object that inherits from the personPrototype
const john = Object.create(personPrototype);
john.name = 'John';
john.age = 30;

// Calling the introduce method on the john object
john.introduce(); 
// Outputs: Hi, my name is John and I am 30 years old.        

In this example, john is a new object created using Object.create, with personPrototype as its prototype. The john object inherits the introduce method from personPrototype and defines its own name and age properties.

Object.prototype.constructor

Object.prototype.constructor is a property that refers to the constructor function that created the instance of the object.

When an object is created using a constructor function or a class, the constructor property is automatically set on the object's prototype.

Here is an example that illustrates the use of Object.prototype.constructor:

// Constructor function for creating Person objects
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Creating an instance of the Person object
const person = new Person('John', 30);

// Accessing the constructor property of the prototype
console.log(person.constructor); 
// Outputs: ? Person(name, age) { this.name = name; this.age = age;}        

In this example, the Person constructor function is used to create an instance of the Person object called person. When we access the constructor property of the person object, it refers to the Person constructor function itself.

The constructor property is often used to check the type of an object and is frequently utilized in JavaScript's prototype-based inheritance to create new instances of the same type.

hasOwnProperty

The hasOwnProperty method in JavaScript is used to check whether an object has a specific property as its own property and not inherited from its prototype chain.

It returns a boolean value indicating whether the object has the specified property.

The syntax for hasOwnProperty is as follows:

object.hasOwnProperty(propertyName)        

  • object: The object on which the hasOwnProperty method is called.
  • propertyName: The name of the property to check for in the object.

Here is an example of using the hasOwnProperty method:

const car = {
  make: 'Toyota',
  model: 'Corolla'
};

console.log(car.hasOwnProperty('make')); // Outputs: true
console.log(car.hasOwnProperty('model')); // Outputs: true
console.log(car.hasOwnProperty('year')); // Outputs: false        

In this example, the car object has the properties make and model. Calling the hasOwnProperty method checks if the object has these properties directly. It returns true for existing properties and false for non-existing properties.

hasOwnProperty is commonly used in JavaScript to distinguish between an object's own properties and those inherited from its prototype chain, ensuring that the property being checked exists directly on the object.

The prototype chain

  • The prototype chain is used for multiple inheritances at different hierarchy levels.
  • We can make a prototypes point to other prototypes using the following method.Here is an example to illustrate the prototype chain in JavaScript:

let student = {
  id: 1,
};
let tution = {
  id: 2,
};
let school = {
  id: 3,
};
student.__proto__ = school; //level1 inheritance
student.__proto__.__proto__ = tution; //level2 inheritance
console.log(student.id); //the student object's property
console.log(student.__proto__.id); //school object's property        

The structure of stdent object will be,

id: 1
[[Prototype]]: Object
id: 2
[[Prototype]]: Object
id: 3
[[Prototype]]: Object        

The output will be based on hierarchy:

1
2        

In this example, the child object is created with parent as its prototype using Object.create. When accessing properties of the child object, if the property is not found directly on the child object, JavaScript looks for it in the parent object due to the prototype chain.

Understanding the prototype chain is crucial for comprehending how inheritance works in JavaScript and is essential for creating efficient and maintainable code using prototype-based inheritance.

Setting Up Prototypical Relationships

A prototypical relationship can be created by performing prototype inheritance. The objects involved in the prototypical relationship are called child and parent.

The child is the object which inherits the properties of the parent object. Let us consider the following example in which the prototype relation is made using the proto prroperty.

let baseuser = {
  read: true,
};
let developer = {
  write: true,
};
developer.__proto__ = baseuser;
console.log(developer.read); //true        

From the example, we can see that the inherited class or the child class can access the properties of the parent class.

Inheriting Methods

The prototype inheritance not only inherits the properties of the object, it also inherits the methods of the object. We can define a function in the parent class and call the function using the child class. We can also add a getter and setter method to the parent class which can be used by the child class.

For example:

let baseuser = {
  //parent object
  read: true,
  job: "",
  showreadpermission: function () {
    console.log(this.read);
  },
  //setter method to set the job of user
  set detail(value) {
    this.job = value;
  },
  //getter method to get job detail
  get detail() {
    return `${this.job}`;
  },
};
let developer = {
  //child object
  write: true,
};
developer.__proto__ = baseuser;
developer.showreadpermission(); //calling parent function
developer.detail = "blogger"; //calling setter method
console.log(developer.detail); //calling getter method
        

Output:

true
blogger        

The getter and setter method in the above code represented by the syntax,

set name(parameter){
    this.property=parameter;
},
object.name=parameter; //calling setter function
get name(){
    //display the properties
}
object.name; //calling getter function.
        

These methods are used to set the value to the properties of the object and also print the values that are currently set to the properties of the object. Using these kinds of getter and setter we can easily modify and display the properties of the parent object.

In our example, the child object is developer and the parent object is base user.

Multi-Tier Inheritance With Scalability

  • We can achieve more scalability in prototype inheritance by including the __proto__ property directly in the class.
  • We can have more complex relations which can be altered very easily when needed. This is a powerful feature of prototype inheritance in javascript. Consider a system having three inheritances such as in the case of the university student.Consider the simple example,

let subject = {
  topic: "javascript",
  about: function () {
    console.log("JS is amazing");
  },
}; //base class
let course = {
  __proto__: subject,
  instructor: "professor",
}; //inherited from subject
let department = {
  __proto__: course,
  dept_name: "IT",
}; //inhserited from course
let student = {
  __proto__: department,
  id: 1,
}; // inherited from department
console.log(student.dept_name);
        

The output will be IT. This way of approach makes inheritance more efficient and simple.

Objects Inherit From Objects

  • On knowing about prototype inheritance, we can say that two or more objects are linked using inheritance to share their properties.
  • This may satisfy the statement object inherits object, but in javascript, this is not completely true.
  • Javascript is a prototype-based language and uses hidden objects called prototypes in inheritance.
  • Therefore, in javascript objects don't directly inherit from objects but use a property called prototype to accomplish inheritance.

Function Prototypes

  • Javascript is completely an object-oriented programming language, where functions are also objects.
  • Therefore, functions also have a prototype and an empty constructor by default.
  • We can add any properties to the prototype of functions by using the function. prototype property.

Consider the simple example,

const course = function () {}; //adding prototype directly
course.prototype.name = "javascript";
const student = new course();
student.id = 1; //adding property
console.log(student);        

Output:

id: 1
[[Prototype]]: Object
name: "javascript"
constructor: ? ()
[[Prototype]]: Object        

We can see that the prototype has been modified and the student also has a unique value called id.

The arrow function in javascript doesn't have a default prototype.

Differential Inheritance

In prototype inheritance, when using the Object.prototype method to assign properties to a prototype, the properties are not actually copied from parent to child but they are just linked with each other. This model is called Differential Inheritance in javascript and is more often referred to as prototype chain.

We can verify this using the hasOwnProperty() method. Let us consider the example of the above section here and check for the name property.

console.log(student.hasOwnProperty("name"));
console.log(student.hasOwnProperty("id"));        

Output:

false
true        

Since student has its own property called id we get true and since only the prototype has the property called name we get false.

Standard Way To Implement Prototypal Inheritance In ES5

The ES5 introduced two differnd mthods such as Object.create() and Object.getPrototypeOf().

  • The object.create has the following syntax and is used to create a new object inheriting the prototype of a given object.

Object.create(object, { newObjectproperties });        

  • The Object.getPrototypeOf() method is used to get the type of prototype from the object.

Let us consider a simple example to understand this further.

let package = {
  version: "2.0",
};
let application = Object.create(package, {
  name: { value: "game" },
}); // inherited from package
console.log(application);
console.log(Object.getPrototypeOf(application));        

This has the following output which only shows the prototype object:

//application object
{name: 'game'}
    name: "game"
[[Prototype]]: Object
    version: "2.0"
[[Prototype]]: Object
// prototype of application object
version: "2.0"
[[Prototype]]: Object        

Extending The Prototype Chain

Every object has a prototype object which can be accessed by Object.prototype property. By using this we can extend a prototype chain.

New initialization

The prototype of a function object can't be directly modified. So we use a function object to assign the prototype of a function to another function object.

function first() {}
first.prototype.value = 2;
function second() {}
second.prototype = first; //second inherits from first
const final = new second();
console.log(final);        

The output denotes the structure of inheritance:

[[Prototype]]: ? first()
arguments: null
caller: null
length: 0
name: "first"
prototype: {value: 2, constructor: ?}
[[FunctionLocation]]: VM1329:1
[[Prototype]]: ? ()        

Object.setPrototypeOf

This method can be used to set the prototype for any object using another object's prototype.

The syntax is:

Object.setPrototypeOf(object, prototype);        

Let us perform the above example using this method.

function first() {}
first.prototype.value = 2;
function second() {}
//making second inherit first
Object.setPrototypeOf(second, first.prototype);
const final = new second();
console.log(final);        

The simplified output will be as follows:

[[Prototype]]: Object
[[Prototype]]: Object
value: 2
constructor: ? first()        

This output has a separate prototype object for the first as it copies the prototype of the first into the second object.

Prototype And Object.getPrototypeOf

The Object.getPrototypeOf() is used to get the prototype of a object specified in the parameter.

Syntax :

Object.getPrototypeOf(object);        

For example :

let one = {
  value: 1,
};
let two = {
  value: 2,
  __proto__: one,
};
// inherited from package
console.log(two);
console.log(Object.getPrototypeOf(two));        

The above example has an object two which is extended from object one using the method of chain linking and the getPrototypeOf() method is used to get the prototype of object two.

// the object two
{value: 2}
value: 2
[[Prototype]]: Object
value: 1
[[Prototype]]: Object
// prototype of the object two
{value: 1}
value: 1
[[Prototype]]: Object        

Conclusion

  • Prototype inheritance in javascript is performed using the prototype property present in every object.
  • The main advantage of performing prototype inheritance is to make methods available in a class to the other classes.
  • The __proto__ property or the Object.create() method can be used to perform prototype inheritance.
  • Multiple inheritance can be done by forming a prototype chain which is more scalable.
  • The prototype chain follows differential inheritance model and this can be verified using the has own property() method.
  • There are predefined ways to implement prototypal inheritance in ES5 using Object.create() and Object.getPrototypeOf().
  • The prototype chain can be extended using new initialization and Object.setPrototypeOf().

Mogili Manikanta

5? @CodeChef (max. 2028) | Knight (top 1.38%) @Leetcode (max. 2153) Solved(1000+) | Specialist @Codeforces (max. 1563) | Competitive Programmer | Java , C++ | FrontEnd(Html ,CSS, JavaScript,React Js, TypeScript, Next Js)

1 个月

Thanks for Sharing

回复

要查看或添加评论,请登录

社区洞察