All about JavaScript Prototypes

A lifelong learner. Love to travel. Listen to music.
Search for a command to run...

A lifelong learner. Love to travel. Listen to music.
No comments yet. Be the first to comment.
Best Practices for Path Structure, Versioning, and Error Handling

Understanding the Performance Implications of async and defer Attributes

Overview static: Default position. It does not allow setting properties like top, bottom, left, right, z-index relative: Acts the same as static. but allows positioning it relatively by putting properties like top, bottom, left, right, z-index absolu...

Overview When an element in the browser has a conflict of styles, the browser uses a set of rules to determine which style should be rendered. This set of rules is defined as CSS Selector Specificity. For instance, for an element, if we have the foll...

Master block, inline, flex, and grid to build better web layouts

The class keyword in JavaScript is syntactic sugar. Internally it uses prototypal inheritance. Prototypal inheritance is like Java or Python classical inheritance. It allows an object to access another object's methods and properties.
It helps
Implement inheritance
Avoid repeating functionalities
Making innovative programming paradigms
Both array and function are objects in JavaScript. So any object, array, and function can use the property of the parent prototyped chain object properties and methods.
Quick Example
Let's run the following code,
const parent = {
foo: () => {
console.log('I am parent!');
},
bar: 'Hello World!'
};
const child = {
foo: () => {
console.log('I am child!');
}
}
child.__proto__ = parent;
child.foo();
console.log(child.bar);
We will see the output,
I am child!
Hello World!
Here, even if the child object does not have the bar property, it will inherit the property from the parent object using the prototype chain.
Let's declare an object
const obj = {};
We will find the base object if we check the prototype chain.
obj.__proto__
const array = [];
Here we created an array. This array object is created by the constructor of an object. We can access these constructors and other array methods like concat, fill, find, push, pop etc using the following,
array.__proto__;
Using the prototypes chain we can access the base object, using the following,
array.__proto__.__proto__;
From this base object, we can access the base object properties and methods like, constructor, hasOwnProperty, toString, valueOf etc.
Now we can use the base object method using the prototypes chain. For example, to use the toString method from the base object, we can write
array.toString();
Since our array is empty, it will return an empty string.
Let's create a method
function a() {}
Using the prototypes chain, we can observe the native function. The native function is the one, that created all other functions.
a.__proto__;
Using the prototypes chain, if we go one step closer, we will find the base object.
a.__proto__.__proto__;
The simplest way to create a prototype is by using the __proto__,
const parent = {
foo: () => {
console.log('I am parent!');
},
bar: 'Hello World!'
};
const child = {
foo: () => {
console.log('I am child!');
}
}
child.__proto__ = parent;
child.foo();
console.log(child.bar);
But while creating our prototypes, we should be aware of the,
We should never use the keyword __proto__ because that impacts execution performance.
The keyword is named intentionally with four underscores, so no one accidentally uses it.
To create the prototypes-chain we can use the Object.create() function.
const human = {
mortal: true
};
const socrates = Object.create(human);
socrates.age = 80;
console.log(`Age of socrates: ${socrates.age}`);
console.log(
`Is human a prototypes of socrates: ${human.isPrototypeOf(socrates)}`
);
This will give us the following output,
Age of socrates: 80
Is human a prototypes of socrates: true
Moment of truth. We will see how we can get benefits from the prototypes.
Let's create two characters,
const dragon = {
name: 'Tanya',
fire: true,
fight() {
return 5;
},
sing() {
return `I am ${this.name}, the breather of fire`;
}
};
const lizard = {
name: 'Kiki',
fight() {
return 1;
}
};
Now we want to use the sing() method of for lizard. But lizard does not have the sing() method. In this case, we have to use the dragon object sing() method.
To use the dragon objects method, we can use the bind property.
const singLizard = dragon.sing.bind(lizard);
singLizard();
This will give the output
I am Kiki, the breather of fire
Now if we update the dragon objects sing() method, that, to sing, the fire property is will be required, then the new dragon object will be,
const dragon = {
name: 'Tanya',
fire: true,
fight() {
return 5;
},
sing() {
if (this.fire) {
return `I am ${this.name}, the breather of fire`;
}
}
};
In this case, if we call singLizard() the sing method will not return the statement.
To return the statement, we have to bind the fire property for the lizard also from the dragon object.
To resolve this issue, instead of binding, we can use the prototypes chain.
lizard.__proto__ = dragon;
lizard.sing();
In this case, when the sing() method will not be found in the lizard object, it will check the prototypes chain and execute the sing() method from there.
After using the prototypes chain, if we invoke the fight() method for the lizard object,
lizard.__proto__ = dragon;
lizard.fight();
This will return
1
Because this fight method already exists in the lizard object. So it will not go to the prototypes-chain for fight() method of dragon object.
We can if the dragon is a prototypes of lizard by the following,
dragon.isPrototypeOf(lizard);
This will be true. And this isPrototypeOf came from the base object, also using the prototype chain.
We will see for object, array and function examples that all the prototypes chain just inherited the property from the prototyped object.
Prototypes Chain With Object
Using the previous example,
const dragon = {
name: 'Tanya',
fire: true,
fight() {
return 5;
},
sing() {
return `I am ${this.name}, the breather of fire`;
}
};
const lizard = {
name: 'Kiki',
fight() {
return 1;
}
};
lizard.__proto__ = dragon;
for (let prop in lizard) {
console.log(
`Is ${prop} lizards own property: ${lizard.hasOwnProperty(prop)}`
);
}
This will give us output
Is name lizards own property: true
Is fight lizards own property: true
Is fire lizards own property: false
Is sing lizards own property: false
So we can see, only name and fight are the lizard own property. Both fire and sing properties have come from the prototypes-chain.
Since it is not copying the properties, this is helpful. For example, if we use sing method of dragon object in multiple places, we can use one single instance. We are not repeating ourselves and saving memory.
Prototypes Chain With Array
let's define an array.
const myArr = [];
We know the JavaScript array has a property called map. Let's check
myArr.hasOwnProperty('map');
This will return false. Because the map come through the prototypes-chain. When we create an array, it is created by the Array object. Using prototypes chain we can access the Array and check the map property existence.
myArr.__proto__.hasOwnProperty('map');
This should return true.
Prototypes Chain With Function
Let's create a function
function myMethod() {}
We know function are a special type of object in Javascript.
So the following statement should return true.
myMethod.hasOwnProperty('call');
But this will return false.
Because, these call, bind, apply methods appear in a method through the prototypes-chain.
The __proto__ of the myMethod linked to the native base function prototypes object.
That base function prototypes object contains all the following,
call
apply
bind
__proto__, this point to the base object of Javascript
Many more ...
myMethod.___proto__.hasOwnProperty('call');
This will return true.
And this __proto__ of the base function prototypes chain to the base object prototype property.
Base object __proto__ of the prototypes property point to the null.
prototype objectLet's create an object and a function and check if there is prototype object.
const obj = {};
function myFunc() {}
console.log(obj.hasOwnProperty('prototype'));
console.log(myFunc.hasOwnProperty('prototype'));
This will return
false
true
Now we know the base object of JavaScript Object has a property property.
Object.hasOwnProperty('prototype');
This will return true.
Actually, the Object of the JavaScript is a function, not an object.
To me, this is the most interesting section. We will go through some real-world scenarios where we can implement the concept of prototypes.
Add a new functionality in Date object
Let's add a new function named lastYear of the Date object, that will return the 1 year earlier than the current year.
Date.prototype.lastYear = function () {
return this.getFullYear() - 1;
};
new Date('1900-10-10').lastYear();
Here we use the function keyword instead of the arrow method to use dynamic-scope instead of lexical-scope.
When using the function keyword, the this context is the new Date.
But with the arrow method the this context is the arrow method itself, given below,
Date.prototype.lastYear = () => {
return this.getFullYear() - 1;
};
new Date('1900-10-10').lastYear();
It will throw an error of,
TypeError: this.getFullYear is not a function
Manipulate the map functionality of the array
We will add the text manipulated before each value of the array,
Array.prototype.map = function (args) {
const newArray = [];
this.forEach(val => newArray.push(`manipulated ${val}`));
return newArray;
};
console.log([1, 2, 3].map());
This will give us output
[ 'manipulated 1', 'manipulated 2', 'manipulated 3' ]
Prototypes are one of the core foundations of JavaScript. Let me know for any queries. Also, you can share your thoughts on how you utilize the prototypes in daily programming.
References: JavaScript: The Advanced Concepts by Andrei Neagoie.