Understanding Prototypes in JavaScript

Classes vs Prototypes

Even though JavaScript is an Object-Oriented Language, there are no classes in JavaScript. This is because JavaScript is a prototype-based language, rather than a class-based language.

What you traditionally thought of as 'JavaScript classes' are actually function constructors.

But what are classes anyways? They are characteristics which can be shared by one or more objects.

In a class-based language, you have classes and other classes can extend (inherit) from it. In this prototype-based model, objects can inherit from other objects.

Prototype

Inheritance is done through a prototype inheritance chain.

Every function has a prototype property.

function Thing() {this.x = 456} // Function
Thing.prototype // Thing {}

Any function can act as a constructor if you create an instance of it using the new keyword.

new Thing(); // Use Thing() as a constructor

When we use the function constructor to construct a new object, the function constructor's prototype is saved into the object's __proto__ property.

To phrase it differently, the prototype is a property of the function constructor object, whereas __proto__ is a property of the objects constructed from the function constructor.

You can also create an object and attached a prototype to it without running the constructor function, by using Object.create(Thing.prototype).

function Human() {this.intelligence = 95}
function Slime() {this.intelligence = 2}
var me = new Human()
me.__proto__ // Human {}
Human.prototype // Human {}
me.__proto__ === Human.prototype // true

You can also use the more standard Object.getPrototypeOf() method to get an object's prototype.

Object.getPrototypeOf(me) === me.__proto__ // true

Prototype Inheritance Chain

When we try to find a property or method from an object, it will search within the object itself first, and if it finds it, return its value.

me.intelligence // 95
me.hasOwnProperty('intelligence') // true
me.name = "Daniel"
me.name // "Daniel"
me.hasOwnProperty('name') // true

When using the function as a constructor, this is an injected implicit reference to the new object being created (Without using the new keyword, this would instead refer to the global object).

Thus, when we specified this.intelligence = 95 inside the constructor, we are assigning the intelligence property of the object being created, and not the prototype.

But if it is not found, it will attempt to find it in its function constructor's prototype, which is the same as the object's __proto__ property.

Human.prototype.species = "Homo sapiens"
me.species // "Homo sapiens"
me.hasOwnProperty('species') // false

Here the me object has no species property, but its constructor does, and so it takes the value from the constructor's prototype's species property.

This lookup will happen down the chain until a property is found; or if not found, return undefined.

me.toString() // function toString() { [native code] }

The toString() method is not a method in either the me object or its function constructor, so it keeps going up the __proto__ chain until it finds the Object.prototype.toString() method, and uses that instead.

me.__proto__.__proto__ === Object.prototype // true
me.__proto__.__proto__.toString === Object.prototype.toString // true

__proto__ is non-standard and a to-be ES6 feature, so not all browsers support this; using the Firefox or Chrome console is your best bet!

Full compatibility comparison can be found in this table

Note on Performance

More specifically, the __proto__ property is an accessor property (getter and setter) which exposes the internal prototype object. This means it is possible to use __proto__ to change the prototype of an object. Though changing the prototype is a slow process and thus bad for performance.

me.__proto__ = Slime.prototype
me.__proto__ // Slime {}
me.species // undefined
Slime.prototype.species = "Fuligo septica"
me.species // "Fuligo septica"

ES6

In ES6, there is a new class keyword, which works similar to the classes found in CoffeeScript. Sebastian Porto wrote an excellent article on it, which you should be able to understand with ease after reading this article!

comments powered by Disqus