Prototypal inheritance; what really happens under the hood.
Tags: javascript, interview, oop, softwareengineering
Published: 2024-02-29 11:43:06

**Everything behaves as an object**
In JavaScript, arrays and functions and various datatypes are considered to be objects or rather behave like one.
An **<u>object</u>** is defined as a composite data type representing a collection of related data and functionality .


They mainly consist of;

- **Properties**: key-value pairs which define the object's characteristics
- **Methods**: functions that are associated with the object and operate on its properties. 

Functions have  _**built in methods**_  such as **'apply'** that calls the function with a given **'this'** value and also has properties such as **_'length'_** which would indicate the number of parameters expected by a given function. 
One can view in-built methods and properties of a function through the console by logging the following;

```
// Log built-in methods of a function
console.log(Object.getOwnPropertyNames(Function.prototype));
```
![Console logging of function methods](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rqv6vls8gd6m9bv90726.png)

```
// Log built-in properties of a function
console.log(Object.getOwnPropertyNames(Function));

```
![Console logging of function properties](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpgjps0bpjkvoaa71hnn.png)



Similarly arrays consist of in built methods such as **'push()'** that adds elements to the end of an array.
They also have a **'length'** property that indicates the number of elements in an array.
One can view an array's in built methods and properties by logging the following

```
// Log built-in methods of arrays
console.log(Object.getOwnPropertyNames(Array.prototype));
```

![Console logging array methods](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shg6na1gxulneo58pm8n.png)

```
// Log built-in properties of arrays
console.log(Object.getOwnPropertyNames(Array));
```

![Console logging array properties](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjl4p7fcmp6rfmhp0wgc.png)


From our earlier definition, it is safe to consider functions and arrays to be objects considering they have in-built key-value pairs (properties) and methods.
For any function or array that is initialized, it inherits this in-built methods from the **'Function.prototype'** and the **'Array.prototype'**.
What is a **prototype**?

---

**Prototypal inheritance** 
A **'Prototype'** is an internal property of objects that _serves as a blueprint for creating new objects_.
_**Prototypal inheritance**_ allows for the **_transfer_** of an objects in built methods and properties to _any instantiated object_.
When a property or method is accessed on an object, JavaScript will first check if the object has that property or method. 
If not it proceeds to move up and looks into the prototype of the object. If the object's prototype does not contain the accessed method or property, JavaScript proceeds to move up once more and check the prototype of our objects' prototype. 
This will go on until either the accessed property is found or the ultimate prototype (**'Object.prototype'**) which contains all properties and methods inherited by all objects in JavaScript is reached.
An attempt to access a property above the 'Object.prototype' will return the **'null'** object which indicates the property or method an object is trying to access does not exist.
The **<u>linking</u>** between an <u>_object_</u> to its <u>_prototype_</u> from which it inherits its properties and methods to another prototype of another object up until the **ultimate prototype** 'Object.prototype' is referred to as a **'prototypal chain'** and is the mechanism enabling **'prototypal inheritance'** which can be defined as **_the inheritance or the referencing of properties and methods from other objects._**
 
![Prototypal chain](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/plbh3ef9t0a01bxy75qy.png)


**Object creation methods and accessing their prototypes**

Objects are created using;

- Object literals
- Constructor functions
- 'Object.create()' 
Each method takes up different syntax and provides varying benefits which we will outline.

---

**Using 'Object literals'** (declaring objects)


For creation of simple, single instances of an object with a fixed set of properties and methods, object literals will be our preferred object creation method.

```
// Creating an object using object literal

var personLiteral = {
  firstName: "Mansa",
  lastName: "Musa",
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
};

console.log(personLiteral.fullName()); // Output: Mansa Musa
```

- **Getting and setting of an objects prototype** 
   used to be carried out using the keyword 'proto' whih has however been deprecated. 

In place of **'proto'**, two new methods provide an explicit way to get and set the prototype of an object;
- **'Object.setPrototypeOf(obj, prototype)'** - sets the prototype of the specified object, in this case **'obj'** to the specified prototype **'prototype'**.
- **'Object.getPrototypeOf(obj)'** - gets the prototype of our specified object **'obj**.

**Constructor functions**
Constructor functions are used in the creation of objects with **_multiple instances_**, **_prototypal inheritance_**, and **_dynamic initialization_** which allows constructor functions to accept parameters during instantiation.

```
// Creating objects using constructor function
function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
  
  this.fullName = function() {
    return this.firstName + " " + this.lastName;
  };
}

// Creating instances of Person
var person1 = new Person("Lucky", "Says");
var person2 = new Person("Bob", "Johnson");

console.log(person1.fullName()); // Output: Lucky  Says
console.log(person2.fullName()); // Output: Bob Johnson

```
Invoking **'new'** instantiates a object from our constructor which inherits the properties of the prototype through a special keyword **prototype** which copies reference of the prototype object to the new object's internal **[[Prototype]]** property of the new instance. This effectively shares properties found on the <u>'prototype object'</u>.
**Mutability of prototypes**
Objects instantiated using Constructor functions can have their behavior <u>dynamically extended</u> by modifying our object prototypes. 

```
 // Define a constructor function
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Add a method to the prototype
Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// Create instances of Person
var person1 = new Person('Alice', 30);
var person2 = new Person('Bob', 25);

// Call the method added to the prototype
person1.greet(); // Output: Hello, my name is Alice and I am 30 years old.
person2.greet(); // Output: Hello, my name is Bob and I am 25 years old.

// Modifying the prototype to add a new property
Person.prototype.gender = 'Female';

// Accessing the new property from instances
console.log(person1.gender); // Output: Female
console.log(person2.gender); // Output: Female

```
 In this example, We add a method <u>**'greet'**</u> to our **Person** prototype and proceed to create two new instances of **Person** and call the '<u>greet</u>' method on them
We then add a new property 'gender' to the **Person** prototype, essentially adding them to all instances of the object before calling them on our two instances **'person1'** and **'person2'**. 
allowing you to even change properties.
 

**Syntactical sugar**
Objects in JavaScript can also be created with **classes**
**Unlike **other languages working with classes, we can add properties to the prototype and hence to every
instantiated object of this constructor;

```
class Rectangle {
  constructor(height, width) {
    this.name = "Rectangle";
    this.height = height;
    this.width = width;
  }
}

class FilledRectangle extends Rectangle {
  constructor(height, width, color) {
    super(height, width);
    this.name = "Filled rectangle";
    this.color = color;
  }
}

const filledRectangle = new FilledRectangle(5, 10, "blue");
// filledRectangle ---> FilledRectangle.prototype ---> Rectangle.prototype ---> Object.prototype ---> null


```
 Classes are referred to as **'syntactical sugar'** as they merely provide a more **_declarative_** and **_readable_** syntax for defining object prototypes.
They also provide more understandable syntax for working with inheritance, with built-in keywords like 'extends' for subclassing and 'super' for calling superclass constructors and methods.

Despite the syntactical difference, classes are eventually _**compiled as constructor functions at runtime**_.

---


**'Object.create()'**
 **'Object.create()'** is a method in JavaScript allowing us to create new objects having **_specified their prototype_**. 


```
// Creating a prototype object
var personPrototype = {
  fullName: function() {
    return this.firstName + " " + this.lastName;
  }
};

// Creating objects using Object.create()
var person1 = Object.create(personPrototype);
person1.firstName = "Emily";
person1.lastName = "Brown";

var person2 = Object.create(personPrototype);
person2.firstName = "David";
person2.lastName = "Lee";

console.log(person1.fullName()); // Output: Emily Brown
console.log(person2.fullName()); // Output: David Lee
```
This explicit and direct assigning of prototypes to objects enables us to create objects without the use of constructor functions, thus eliminating any **constructor overhead** leading to cleaner and more concise code that does not require instantiation-time initialization logic.

---

**Performance concerns**
**Prototypical inheritance** provides great
dynamicity to code and reduces the need for repetitive codebases. It should however be undertaken with care so as to avoid accumulating **overhead**.
Every attempt to access properties high up on the prototype chain incurs a **negative impact** on code performance.
To avoid unnecessary performance drops, one may consider checking if the desired property exists on _itself_ using <u>**'hasOwnProperty'**</u> or <u>**'Object.hasOwn'**</u> before proceeding to traverse the prototype chain.
It is also advisable to check if the desired property exists as trying to access nonexistent properties leads to full traversal of the full prototype chain.