OOP in JS: Prototypes

The concept of prototype is a peculiar feature of the JavaScript language.

I’m going to wear the “old guy” hat here, and tell you that before 2015, we didn’t have classes in JavaScript. And we didn’t have the easy inheritance they provide.

But we could still implement some kind of inheritance using prototypes.

Now, in 2021 you likely won’t use prototypes directly in your code, but that’s how JavaScript classes work under the hood, so it’s still something you should know as a JavaScript programmer.

Not to mention you need to understand when you read code that uses them.

Constructor functions

Before introducing prototypes I need to introduce constructor functions.

In JavaScript we can use the new keyword to create objects using a function that’s called constructor function. It’s just a function but we use it to create an object constructor, in this way:

function Car() {
  this.color = 'blue'
}

By convention, we use an uppercase letter to define the function, but it’s not mandatory

Remember constructors when we talked about classes? It’s basically the same thing, but with a different syntax.

Now we can initialize new objects using the new keyword, like this:

const tesla = new Car()
const bmw = new Car()

Each of those objects points to a prototype

Every object in JavaScript has a prototype property that points to its prototype.

What is the prototype of tesla and bmw? It’s Car.

Both objects have the color property, as that’s set in the Car constructor.

See this special __proto__ property? That points to the object’s prototype.

What’s a prototype useful for?

Now here’s the fun part.

You can add other properties to the prototype:

function Car() {
  this.color = 'blue'
}

Car.prototype.owner = 'Flavio'

Now both objects have the owner property, too.

const tesla = new Car()
const bmw = new Car()

tesla.owner //'Flavio'
bmw.owner //'Flavio'

If you assign the owner property a new value in one object:

tesla.owner = 'test'

The other object is independent:

bmw.owner //'Flavio

But if you set it like this:

tesla.__proto__.owner = 'test'

then bmw.owner is test too.

So basically Car is a common object that both instances inherit from.

Utility methods

JavaScript provides some utility methods to work with prototypes:

Object.getPrototypeOf(tesla) === Car.prototype //true

Car.prototype.isPrototypeOf(tesla) //true

And the prototype of Car is Object:

Object.prototype.isPrototypeOf(Car)

which is also the prototype of tesla and bmw, because it’s a chain:

Object.prototype.isPrototypeOf(tesla) //true
Object.prototype.isPrototypeOf(bmw) //true

Object is the prototype of Car which is the prototype of tesla and bmw.

The chain ends at the Object object, which is a special snowflake.

An example with an array

If you initialize an array

const list = []

the prototype is Array.

You can verify this by checking with the Object.getPrototypeOf() and the Object.prototype.isPrototypeOf() methods:

const list = []

Object.getPrototypeOf(list) === Array.prototype
Array.prototype.isPrototypeOf(list)

All the properties and methods of the prototype are available to the object that has that prototype.

Car has all the methods and properties provided by Object.

list has all the methods and properties provided by Array, PLUS all the methods and properties provided by Object, because Object.prototype is the base prototype of all the objects:

Using prototypes to write more efficient code

One thing that is often mentioned when introducing prototypes is that if you have a constructor for an object with a method, like this:

function Car() {
  this.drive = () => {
  //do lots of expensive work
    console.log('drive!')
  }
}

const tesla = new Car()

tesla.drive()

and you expect to have many instances of that object, and that function is heavy meaning it’s wasting memory, you can extract that method to the prototype:

function Car() {
  
}

Car.prototype.drive = () => {
  //do lots of expensive work
  console.log('drive!')
}

const tesla = new Car()

tesla.drive()

So JavaScript instead of having 1000 functions for 1000 objects, it has just 1 for the prototype of those 1000 objects.

It’s not something I’ve done myself, as I don’t do any kind of high intensive applications, and I think it’s premature optimization to think about doing this kind of work to make the computer run faster.

But it’s worth knowing you have this possibility.

Lessons in this unit:

0: Introduction
1: Classes
2: Class methods
3: Private class properties
4: Constructors
5: Inheritance
6: ▶︎ Prototypes
Want to learn more? Check out our courses