In programming, inheritance refers to passing down characteristics from a parent to a child so that a new piece of code can reuse and build upon the features of an existing one. JavaScript implements inheritance by using objects. Each object has an internal link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. By definition, null
has no prototype and acts as the final link in this prototype chain.
Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain. The chain ends when we reach a prototype that has null
for its own prototype.
When you try to access a property of an object: if the property can't be found in the object itself, the prototype is searched for the property. If the property still can't be found, then the prototype's prototype is searched, and so on until either the property is found, or the end of the chain is reached, in which case undefined
is returned.
So when we call myObject.toString()
, the Javascript engine follows the prototype chain to resolve the method:
- looks for
toString()
method inmyObject
- if the Javascript engine can't find it there, the Javascript engine looks into the prototype object of
myObject
fortoString()
method - if the Javascript engine finds it there, the Javascript engine calls the
toString()
method.
In JavaScript, __proto__
and prototype
are two related but distinct concepts that are often confused with each other.
__proto__
(also called the Dunder Proto or Double Underscore Prototype) is a property of an object that points to its prototype. This property is used internally by the JavaScript engine to search for properties and methods on an object's prototype chain.
The __proto__
is not part of the ECMAScript standard, but is supported by browsers.
Object.getPrototypeOf()
and Object.setPrototypeOf()
are the modern ways of getting access to and setting an object's prototype.
Let's create an object literal:
const myObject = {
name: "John Doe",
greet() {
console.log(`Hello ${this.name}`);
},
};
myObject.greet(); // Hello John Doe
myObject.__proto__ // Object { }
Object.getPrototypeOf(myObject); // Object { }
Object.prototype
is the most basic prototype, that all objects have by default. The prototype of Object.prototype
is null
, so it's at the end of the prototype chain.
The prototype of an object is not always Object.prototype
. For example:
const myDate = new Date();
let object = myDate;
do {
object = Object.getPrototypeOf(object);
console.log(object);
} while (object);
// Date.prototype
// Object { }
// null
prototype
is a built-in property when you create an object from a constructor function. This is like a blueprint for all the objects created from that constructor function. It defines the shared properties and methods that all instances of that object will have.
Let's create a constructor function:
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getArea = function() {
return Math.PI * Math.pow(this.radius, 2);
};
const circle1 = new Circle(5);
circle1.getArea(); // 78.53981633974483
const circle2 = new Circle(6);
circle2.getArea(); // 113.09733552923255
console.log(circle1.__proto__ === Circle.prototype); // true
console.log(circle2.__proto__ === Circle.prototype); // true
In the code above, the getArea
method is contained in an object called Circle.prototype
. When a new Circle
instance is created, its __proto__
property is set to Circle.prototype
.
circle1.getCircumference = function () {
return Math.PI * 2 * this.radius;
}
circle1.getCircumference(); // 31.41592653589793
circle2.getCircumference(); // Uncaught TypeError: circle2.getCircumference is not a function
In the code above, the getCircumference()
method is added to only circle1
object and not on Circle.prototype
or circle2
.
Here we are comparing the __proto__
(which is the prototype from which the object was created), with the prototype
(a property that only exists in constructor functions).
const fn = () => {};
fn.__proto__ === Function.prototype; // Output: true
Every function has a prototype property that points to the Function.prototype
object. fn
is an anonymous function created using the arrow function syntax. This means that fn
inherits properties and methods from Function.prototype
. For example, fn
can use methods like call()
, apply()
, and bind()
because they are defined on Function.prototype
.
const fn = () => {};
fn.__proto__ === new Function().__proto__ // Output: true
fn
is an anonymous function created using the arrow function syntax. new Function()
creates a new function object using the Function constructor. The __proto__
property of fn
points to the same object as the __proto__
property of the newly created function object using new Function()
.
const arr = [];
arr.__proto__ === Array.prototype; // Output: true
Every array in JavaScript has a prototype property that points to the Array.prototype
object. arr
is an array created using the array literal syntax []
. This means that arr
inherits properties and methods from Array.prototype
, such as push()
, pop()
, and forEach()
.
const arr = [];
arr.__proto__ === new Array().__proto__; // Output: true
arr
is an array created using the array literal syntax []
. new Array()
creates a new array object using the Array constructor. The __proto__
property of arr
points to the same object as the __proto__
property of the newly created array object using new Array()
.
const obj = {};
obj.__proto__ === Object.prototype; // Output: true
Every object in JavaScript has a prototype property that points to the Object.prototype
object. obj
is an object created using the object literal syntax {}
. This means that obj
inherits properties and methods from Object.prototype
, such as toString()
and hasOwnProperty()
.
const obj = {};
obj.__proto__ === new Object().__proto__; // Output: true
obj
is an object created using the object literal syntax {}
. new Object()
creates a new object using the Object constructor. The __proto__
property of obj
points to the same object as the __proto__
property of the newly created object using new Object()
.
const str = 'Hello';
str.__proto__ === String.prototype; // Output: true
Strings in Javascript have a prototype property that points to the String.prototype
object. str
is a string created using the string literal syntax 'Hello'
. This means that str
inherits properties and methods from String.prototype
, such as toUpperCase()
and substr()
.
const str = 'Hello';
str.__proto__ === String.prototype; // Output: true
str
is a string created using the string literal syntax 'Hello'
. new String()
creates a new string object using the String constructor. The __proto__
property of str
points to the same object as the __proto__
property of the newly created string object using new String()
.
const num = 123;
num.__proto__ === Number.prototype; // Output: true
Numbers in JavaScript have a prototype property that points to the Number.prototype
object. num
is a number created using the number literal syntax 123. This means that num
inherits properties and methods from Number.prototype
, such as toFixed()
and toExponential()
.
const num = 123;
num.__proto__ === new Number().__proto__; // Output: true
num
is a number created using the number literal syntax 123. new Number()
creates a new number object using the Number constructor. The __proto__
property of num
points to the same object as the __proto__
property of the newly created number object using new Number()
.
const bool = true;
bool.__proto__ === Boolean.prototype; // Output: true
Booleans in JavaScript have a prototype property that points to the Boolean.prototype
object. bool
is a boolean created using the boolean literal syntax true
. This means that bool
inherits properties and methods from Boolean.prototype
, such as toString()
and valueOf()
.
const bool = true;
bool.__proto__ === new Boolean().__proto__; // Output: true
bool
is a boolean created using the boolean literal syntax true
. new Boolean()
creates a new boolean object using the Boolean constructor. The __proto__
property of bool
points to the same object as the __proto__
property of the newly created boolean object using new Boolean()
.