Appearance
Javascript Object Prototype and Inheritance
This article outlines the important points from MDN's Object Inheritance and prototype chain article and other relevant observations.
Prototype Chain
In an inheritance chain, objects are linked to each other through its prototype.
Note
The property of an object that points to its prototype is not called prototype. Its name is not standard, but in practice all browsers use __proto__
. The standard way to access an object's prototype is the Object.getPrototypeOf()
method.
For the purpose of this article, we'll refer to an object's prototype with __proto__
.
Setting up Prototype Chains
Function Constructor
Basic Person
example:
js
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello ${this.name}`);
};
const john = new Person('john');
john.greet(); // "Hello john"
The above is functionally equivalent to the following in ES6 Class syntax:
js
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello ${this.name}`);
}
}
prototype
and __proto__
Some important observations following the basic Person
example above.
js
john.__proto__
// Returns
{
constructor: function Person(name) {...}
greet: function greet() {...}
}
john.prototype
// Returns
undefined
Person.__proto__
// Returns
function() // The most basic function prototype that all functions have by default
Person.prototype
// Returns
{
constructor: function Person(name) {...}
greet: function greet() {...}
}
When an instance is created with Person()
, the instance's __proto__
property now points to the constructor's prototype
. Note that Person.prototype.constructor
is created by default and points to the constructor function itself.
+++
More examples:
js
const ob = { foo: "bar" }
function func() {}
obj.prototype
// Returns
undefined
func.prototype
// Returns
{
constructor: function func() {...}
}
The example above emphasizes that prototype
property only exists for functions by default.
Prototypes Are Passed by Reference
js
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello ${this.name}`);
};
Person.prototype.foo = 'bar';
var john = new Person('john');
john.greet(); // "Hello john"
console.log(john.foo); // "bar"
// Reassign prototype properties
Person.prototype.greet = function () {
console.log('greet func overwritten');
};
Person.prototype.foo = 'foo overwritten';
john.greet(); // "greet func overwritten"
console.log(john.foo); // "foo overwritten"
Setting up Longer Inheritance Chains
js
function Parent() {}
Parent.prototype.parentFunc = function () {
console.log('parentFunc');
};
function Child() {}
Child.prototype.childFunc = function () {
console.log('childFunc');
};
// Sets the `Child.prototype.__proto__` to `Parent.prototype`
Object.setPrototypeOf(Child.prototype, Parent.prototype);
const childInstance = new Child();
childInstance.parentFunc(); // "parentFunc"
childInstance.childFunc(); // "childFunc"
// The prototype chain of childInstance
{
__proto__: {
anotherFunc: function anotherFunc() {...},
consturctor: function Child() {...},
__proto__: {
parentFunc: function parentFunc() {...},
constructor: function Parent() {...},
__proto__: ...
}
}
}
WARNING
Object.setPrototypeOf
is not performant
In ES6 Class terms, the above is functionally equivalent to
js
class Parent {...}
class Child extends Parent {...}
const childInstance = new Child()
Object.create()
js
const parent = { parent: 'parent' };
// Points `child.__proto__` to `parent`
const child = Object.create(parent);
child.foo = 'bar';
// child object
{
foo: 'bar',
__proto__: {
parent: 'parent',
},
};
const grandchild = Object.create(child);
grandchild.foofoo = 'barbar';
// grandchild object
{
foofoo: 'barbar',
__proto__: {
foo: 'bar',
__proto__: {
parent: 'parent'
}
}
}
Usage with Typescript
If we define object inheritance without ES6 Classes, Typescript intellisense and type checks for inherited properties do NOT work.