Prototype in JavaScript
What is Prototype?
In JavaScript, every function and object has a prototype. The prototype is an object that is associated with every function and object by default in JavaScript, and it allows for inheritance and shared properties across instances of objects.
JavaScript's Prototypal Behavior
JavaScript uses prototypal inheritance, meaning objects can inherit properties and methods from other objects. Instead of using classes as in classical inheritance, JavaScript allows objects to be linked to a prototype object.
Why this
Doesn't Work in Arrow Functions
Arrow functions do not have their own this
. Instead, they inherit this
from their lexical scope. This means that when you use this
inside an arrow function, it does not refer to the object it is called on but rather to the scope where the function was defined.
const obj = {
name: 'Priyanshu',
greet: () => {
console.log(this.name); // undefined
}
};
obj.greet();
In the above example, this.name
is undefined
because arrow functions do not have their own this
, and it does not refer to obj
.
Everything in JavaScript is an Object
In JavaScript, almost everything is an object or can be treated as an object. Primitive values (except null
and undefined
) are wrapped in their respective object types (e.g., Number
, String
).
Is Function in JavaScript an Object?
Yes, functions in JavaScript are also objects. They can have properties and methods, including the prototype
property, which enables inheritance.
function greet() {}
console.log(typeof greet); // 'function'
console.log(greet instanceof Object); // true
What is this
?
this
refers to the context in which a function is called. The value of this
depends on how the function is executed.
- In a regular function,
this
refers to the global object (window
in browsers,global
in Node.js) unless used inside an object. - In a method of an object,
this
refers to the object itself. - With the
new
keyword,this
refers to the newly created object.
Example:
function Person(name) {
this.name = name;
}
const user = new Person('John');
console.log(user.name); // 'John'
Sample Code with Theoretical Knowledge
Example 1: Function Prototype
function createProduct(name, price) {
this.name = name;
this.price = price;
}
createProduct.prototype.increment = function () {
this.price++;
};
createProduct.prototype.printMe = function () {
console.log(`price of ${this.name} is $${this.price}`);
};
const tea = new createProduct('tea', 25);
const coffee = new createProduct('coffee', 250);
coffee.printMe();
Example 2: Extending Built-in Prototypes
String.prototype.trueLength = function () {
console.log(`${this}`);
console.log(`True length is: ${this.trim().length}`);
};
const userName = 'JohnDoe ';
userName.trueLength();
Interesting Case: Prototype Chain and Custom Properties
let myHeroes = ['Shaktiman', 'Balveer'];
let heroPower = {
canFly: true,
canRun: true,
getGreeting: function () {
console.log(`The hero ${this.canFly ? 'can' : 'can not'} fly`);
}
};
Object.prototype.newProp = function () {
console.log('new Property');
};
Array.prototype.newArrayProp = function () {
console.log('new Array Property');
};
myHeroes.newProp(); // Works because Array inherits from Object
heroPower.newProp(); // Works because heroPower is an object
myHeroes.newArrayProp(); // Works because it's an array
heroPower.newArrayProp(); // Fails because heroPower is not an array
Inheritance using Prototype
const User = {
name: 'chai',
email: 'chai@google.com'
};
const Teacher = {
makeVideo: true
};
const TeachingSupport = {
isAvailable: false
};
const TASupport = {
makeAssignment: 'JS assignment',
fullTime: true,
__proto__: TeachingSupport
};
// Legacy syntax
Teacher.__proto__ = User;
// Modern syntax
Object.setPrototypeOf(TeachingSupport, Teacher);
Sample Code with Theoretical Knowledge
Example 1: Function Prototype
function createProduct(name, price) {
this.name = name;
this.price = price;
}
createProduct.prototype.increment = function () {
this.price++;
};
createProduct.prototype.printMe = function () {
console.log(`price of ${this.name} is $${this.price}`);
};
const tea = new createProduct('tea', 25);
const coffee = new createProduct('coffee', 250);
coffee.printMe();
Here's what happens behind the scenes when the new keyword is used:
-
A new object is created: The new keyword initiates the creation of a new JavaScript object.
-
A prototype is linked: The newly created object gets linked to the prototype property of the constructor function. This means that it has access to properties and methods defined on the constructor's prototype.
-
The constructor is called: The constructor function is called with the specified arguments and this is bound to the newly created object. If no explicit return value is specified from the constructor, assumes this, the newly created object, to be the intended return value.
-
The new object is returned: After the constructor function has been called, if it doesn't return a non-primitive value (object, array, function, etc.), the newly created object is returned.
Explanation:
- The function
createProduct
serves as a constructor function. - The methods
increment
andprintMe
are added to the prototype, making them shared among all instances. new
creates a new object withcreateProduct.prototype
as its prototype.
Example 2: Extending Built-in Prototypes
String.prototype.trueLength = function () {
console.log(`${this}`);
console.log(`True length is: ${this.trim().length}`);
};
const userName = 'JohnDoe ';
userName.trueLength();
Explanation:
trueLength
is added toString.prototype
, allowing all strings to use this method.- The method trims the string and calculates the actual length, demonstrating prototype extension.
Interesting Case: Prototype Chain and Custom Properties
let myHeroes = ['Shaktiman', 'Balveer'];
let heroPower = {
canFly: true,
canRun: true,
getGreeting: function () {
console.log(`The hero ${this.canFly ? 'can' : 'can not'} fly`);
}
};
Object.prototype.newProp = function () {
console.log('new Property');
};
Array.prototype.newArrayProp = function () {
console.log('new Array Property');
};
myHeroes.newProp(); // Works because Array inherits from Object
heroPower.newProp(); // Works because heroPower is an object
myHeroes.newArrayProp(); // Works because it's an array
heroPower.newArrayProp(); // Fails because heroPower is not an array
Explanation:
newProp
is added toObject.prototype
, so all objects and arrays inherit it.newArrayProp
is added toArray.prototype
, so only arrays can access it.- Prototype chaining allows properties of
Object.prototype
to be accessible in arrays.
Inheritance using Prototype
const User = {
name: 'chai',
email: 'chai@google.com'
};
const Teacher = {
makeVideo: true
};
const TeachingSupport = {
isAvailable: false
};
const TASupport = {
makeAssignment: 'JS assignment',
fullTime: true,
__proto__: TeachingSupport
};
// Legacy syntax
Teacher.__proto__ = User;
// Modern syntax
Object.setPrototypeOf(TeachingSupport, Teacher);
Explanation:
- The
__proto__
property establishes prototype relationships manually (legacy way). Object.setPrototypeOf()
is the modern and recommended approach.TeachingSupport
inherits fromTeacher
, andTeacher
inherits fromUser
.