To make you comfortable with the word Prototype I recommend you to think of it as a Family Tree where you inherit some properties from your parents like hair color, height, blood type, skin tone, etc. to yourself. In the same way JavaScript objects inherits properties and methods from there prototype (or parent):
Date
objects inherit fromDate.prototype
Array
objects inherit fromArray.prototype
Object
inherit fromObject.prototype
NOTE
Note
The Chrome console uses [[Prototype]] to denote the object's prototype(or parent), following
the ECMAScript terms; Firefox uses <prototype> to denote the object's prototype(or parent).
For consistency I will use [[Prototype]].
Before going forward I want to tell you some important terminologies that I will use in this blog, detailed explanation is given in later section:
[[Prototype]]/<prototype>
This notation is used in browsers to represent who is the parent (or prototype) of the object. It does not set the parent (or prototype) of the object or get the parent (or prototype) of the object it simply tells who is the parent (or prototype) of object.
__proto__
It is a property in JavaScript that has the power to set the parent (or prototype) of the object or get the parent (or prototype) of the object.
.prototype
It is a special property that is only available on a constructor function and it becomes the parent (or prototype) of every object that is created from that constructor function.
Prototype and Prototype Chain
Let understand what is Prototype and Prototype Chain in JavaScript.
In JavaScript, every object has a special hidden property [[Prototype]]
(that tell who is parent of this object) that is either null
or references another object.
When we try to access any property of an object and that property is not available on the object, than JavaScript does not stop here it then start searching the parent (or prototype) of the object, the parent (or prototype) of the parent (or prototype), and so on until either a property with a matching name is found or the end of the prototype chain is reached.
let obj = {
name: "object"
}
console.log(obj.name) // logs: object
// But if we try to access an property that is not
// available on the object than it's prototype is
// searched which in this case is Object.prototype
console.log(obj.toString()) // logs: [object Object]
// In above console.log() we try to access toString() function which is not
// available in "obj" but as we know JS does not stop here and it start searching
// the prototype of "obj" which is "Object.prototype" and it has a function defined
// by name toString() so JS takes this function from prototype and execute it here.
// The Prototype (or parent) chain for above example will look like this
{name: "object"} ---> Object.prototype ---> null
Prototype (or parent) chain is nothing but it simply is that JS start searching the parent (or prototype) of the object when it does not find any property or method on the object and it find it until the property of matching name is found or null
is reached which is end of prototype chain so this searching of parent (or prototype) of object in JS creates a chain of object to which we refer as prototype chain.
For example in above code when JS does not find toString()
method on obj
it start searching the parent (or prototype) of obj
which is Object.prototype
and found a method name toString()
if JS does not found a method name toString()
on Object.prototype
than it will search for the parent (or prototype) of Object.prototype
which is in this case null
and it will return undefined as null
does not have method name toString()
. This searching of method or property on parent (or prototype) of object creates a chain of object which is refer to as Prototype Chain.
Prototype Chain of above example.
Another example for this is:
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal. (This is explained in later section.)
__proto__: {
b: 3,
c: 4,
},
};
// o.[[Prototype]] has properties b and c because {b: 3, c: 4} is parent (or prototype) of
// "o" object.
// o.[[Prototype]].[[Prototype]] is Object.prototype because every object has "Object.prototype"
// as it parent if we don't change the prototype chain.
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? (which is this object { b: 3, c: 4 } )
// No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and
// there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.
we will learn more about __proto__ in later section and in above example we are using o.[[Prototype]]
this notation to get object parent (or prototype) but in real use cases we use Object.getPrototypeOf()
to get the parent (or prototype) of an object and Object.setPrototypeOf()
to set the parent (or prototype) of an object. we will learn more about them in later section.
Similarly, we can create longer prototype chains, and a property will be sought on all of them.
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal.
__proto__: {
b: 3,
c: 4,
__proto__: {
d: 5,
},
},
};
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> { d: 5 } ---> Object.prototype ---> null
console.log(o.d); // 5
Some Important terminologies of Prototypes
__proto__
.prototype
Object.getPrototypeOf()
Object.setPrototypeOf()
__proto__
The __proto__
accessor property of an Object instances is used to set the parent (or prototype) of the object or get the parent (or prototype) of the object.
For Example
Here, we are using
__proto__
accessor property to get the prototype(or parent) of the object which return is simply the reference to[[Prototype]]
hidden property of an object.For objects created using an object literal this value is
Object.prototype
.For objects created using array literals, this value is
Array.prototype
.For functions, this value is
Function.prototype
.
// OBJECTS
const obj = {
a: 1,
b: 2,
}
console.log(obj.__proto__)
// obj prototype
{__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,
__lookupSetter__: ƒ, …}
// ARRAYS
const arr = [1, 2, 3, 4]
console.log(arr.__proto__)
// arr prototype
[at: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
// Functions
function hello() {
return "Hello"
}
console.log(hello.__proto__)
// hello function prototype
{apply: ƒ, bind: ƒ, call: ƒ, arguments: (...), …} (only accessible in firefox console)
The
__proto__
also allow to change the parent (or prototype) of an object like:const obj = { a: 1, b: 2, } const objTwo = { c: 3, d: 4, } const objThree = { e: 5, f: 6, } obj.__proto__ = objTwo // doing this change the prototype of obj from Object.prototype // (shown in above example) to objTwo ({ c: 3, d: 4}). objTwo.__proto__ = objThree console.log(obj.__proto__) // "obj" Object Prototype // {c: 3, d: 4} console.log(objTwo.__proto__) // {e: 5, d: 6} console.log(objThree.__proto__) // {__defineGetter__: ƒ, __defineSetter__: ƒ, …}
But there is one thing to keep in mind here is that the value passed to
obj.__proto__
must be an object ornull
. Providing any other value toobj.__proto__
will do nothing.const obj = { a: 1, b: 2, } obj.__proto__ = "hello" // OR // obj.__proto__ = 66 // It will do nothing. console.log(obj.__proto__) // obj prototype is still this {__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …}
But setting or reading the prototype with obj.__proto__
is considered outdated and somewhat deprecated (moved to the so-called “Annex B” of the JavaScript standard, meant for browsers only).
The modern methods to get/set a prototype are:
Object.getPrototypeOf(obj)
– returns the[[Prototype]]
ofobj
.Object.setPrototypeOf(obj, proto)
– sets the[[Prototype]]
ofobj
toproto
.
The only usage of __proto__
, that’s not frowned upon, is as a property when creating a new object: { __proto__: ... }
.
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal.
__proto__: {
b: 3,
c: 4,
},
};
.prototype
In JavaScript, .prototype
is a special property that exists only on the functions that can be used as a constructor function. It is not accessible on the instances of objects.
Who can access .prototype
property
Every function that is meant to be used as a constructor function can access this property.
Constructor Functions
function Person() {} console.log(Person.prototype). // { constructor : ƒ Person(),[[Prototype]]: Object }
Classes
class Student {} console.log(Student.prototype) // { constructor : class Student,[[Prototype]]: Object }
Built-in constructors
Who cannot access .prototype
property
These are functions, objects and methods that cannot be used as a constructor function so they cannot access the .prototype
property.
Arrow Function
const arrowFun = () => {} console.log(arrowFun.prototype) // undefined
Object and Class instances
const obj = {} class Car {} const myCar = new Car() console.log(myCar.prototype) // undefined console.log(obj.prototype) // undefined
Methods inside objects or classes
const person = { speak() {} }; console.log(person.speak.prototype); // undefined
async Functions
async function AsyncFunction() {} console.log(AsyncFunction.prototype) // undefined
What is .prototype
property ?
The .prototype
property of a constructor function is used to set the parent (or prototype) of the every new object that is being created from the constructor function.
function Student(name, age) {
this.name = name;
this.age = age;
}
Student.prototype.name = function() {
return `My name is ${this.name}.`
}
Student.prototype.age = function() {
return `My age is ${this.age}.`
}
const studentOne = new Student("John", 33)
const studentTwo = new Student("Jane", 23)
const studentThree = new Student("Jo", 30)
console.log(studentOne) // Object { name: "John", age: 33 }
console.log(studentTwo) // Object { name: "Jane", age: 23 }
console.log(studentThree) // Object { name: "Jo", age: 30 }
// Since "obj" is created from the Student Constructor function so it's parent (or prototype)
// is the constructor function .prototype property
console.log(Object.getPrototypeOf(studentOne)) // Object { name: name(), age: age(), constructor: function Student(name, age), ... }
console.log(Object.getPrototypeOf(studentTwo)) // Object { name: name(), age: age(), constructor: function Student(name, age), ... }
console.log(Object.getPrototypeOf(studentThree)) // Object { name: name(), age: age(), constructor: function Student(name, age), ... }
console.log(Object.getPrototypeOf(studentOne) === Student.prototype) // true
console.log(Object.getPrototypeOf(studentTwo) === Student.prototype) // true
console.log(Object.getPrototypeOf(studentThree) === Student.prototype) // true
If that a too much to absorb let me sum up for you:
[[Prototype]]/<prototype>
This notation is used in browsers to represent who is the parent of the object. It does not set or get the prototype it simply tells who is the prototype of object.
__proto__
It is a property in JavaScript that has the power to get or set the
[[Prototype]]
(or parent) of the object..prototype
It is a special property that is only available on a constructor function and it becomes the prototype(or parent) of every object that is created from a constructor function.
Conclusion
JavaScript's prototype chain is a powerful mechanism that enables objects to inherit properties and methods efficiently. Understanding how the prototype chain works helps in debugging, improving performance, and writing cleaner, more maintainable JavaScript.