DEV Community

Cover image for Understanding classes and prototypes
João Guilherme do Amaral Vequiato
João Guilherme do Amaral Vequiato

Posted on • Updated on

Understanding classes and prototypes

In this article I'll try approach in a simple and objective way what I've learned (thanks to Will Sentance) about object orientend in Javascript, wich is a very popular program paradigm to structure complex codes.

I'll initiate this with the principle of encapsulation, in how we can store data and their respective methods: Objects.

Let's start with the object vehicle.

const vehicle1 = {
  type: "Car",
  name: "Belina",
  speed: 0,  
  accelerate: function() { vehicle.speed++; }
};
Enter fullscreen mode Exit fullscreen mode

Supose we need now to create a new vehicile, let do this with a diferent ways.

Using the dot notation 🔨

const vehicle2 = {};; // creates a empty object
// assigns the properties to the object
vehicle.type = "Car";
vehicle.name = "Monza";
vehicle.speed = 0;
vehicle.accelerate = function() {
  vehicle2.speed++;
}
Enter fullscreen mode Exit fullscreen mode

Using Object.create() 🛠️

const vehicle3 = Object.create(null);
// assigns the properties to the object
vehicle3.type = "Car";
vehicle3.name = "Uno";
vehicle3.speed = 0;
vehicle3.accelerate = function() {
  vehicle3.speed++;
}
Enter fullscreen mode Exit fullscreen mode

All right, every above ways be valids to create an object, however you may be feeling uncomfortable with the ways we're did that.

We're break a important principle in programming: Don't Repeat Yourself

What we can do?

Factories! Generating objects with functions 🏭

function vehicleCreator(type, name, speed) {
  const newVehicle = {};
  newVehicle.type = type;
  newVehicle.name = name;
  newVehicle.speed = 0;
  newVehicle.accelerate = function () {
    newVehicle.speed++;
  };
  return newVehicle;
};
Enter fullscreen mode Exit fullscreen mode

Now we get eliminate the code copy every time we need to create a new vehicle.

const vehicle1 = vehicleCreator("Car", "Belina", 0);
const vehicle2 = vehicleCreator("Car", "Monza", 0);
vehicle1.accelerate();
Enter fullscreen mode Exit fullscreen mode

We solve some of our problems. However, every time we've created a new vehicle we created unnecessary copies in the application memory of the accelerate method. That's sucks!

Using prototype chain ⛓️

What if instead of creating a new method copy on each new object, we store our methods in other local and create a link these methods in the object, in a way what the interpreter can check the prototype chain and find them.

We can creat this link using Object.create()

function vehicleFunctionStore() {
  accelerate: function() { this.speed++; }
}

function vehicleCreator(type, name, speed) {
  // the Object.create() serve to do the link between the new object created and the object methods vehicleFunctionStore.
  const newVehicle = Object.create(vehicleFunctionStore);
  newVehicle.type = type;
  newVehicle.name = name;
  newVehicle.speed = 0;
  return newVehicle;
}
Enter fullscreen mode Exit fullscreen mode

Now, the way we created and used the methods of each object stay the same, however were not created unecessary copies of the method.

const vehicle1 = vehicleCreator("Car", "Belina", 0);
const vehicle2 = vehicleCreator("Car", "Monza", 0);
vehicle1.accelerate();
Enter fullscreen mode Exit fullscreen mode

But, how this link with the objects works?

All Javascript object has one property called proto.
This property links all Javascript objects to a another big one, the Object.prototype that contains a built-in methods.

It's exactly through this property we can access our accelerate method linked by Object.create()

// When we execute the accelerate method, the interpreter goes to the object vehicle1 search for the method. However, this method doesn't exist in the object. 
So, the interpreter goes to the Object.Prototype to verifies if the method exists, and there can find it and execute it!

vehicle1.accelerate();
Enter fullscreen mode Exit fullscreen mode

Great! We solve one more problem. But the things still out of the pattern here.
This approach is useful and works, but exists another ways to facilitate our job.

The "new" keyword 🆕

// Let's change this way
const vehicle1 = vehicleCreator("Car", "Belina", 0);
const vehicle2 = vehicleCreator("Car", "Monza", 0);

// To this
const vehicle1 = new vehicleCreator("Car", "Belina", 0);
const vehicle2 = new vehicleCreator("Car", "Monza", 0);
Enter fullscreen mode Exit fullscreen mode

However we need to do some adaptations in our code.

function vehicleCreator(type, name, speed) {
  this.type = type;
  this.name = name;
  this.speed = speed;
}
Enter fullscreen mode Exit fullscreen mode

Notice now we don't have to create a new object neither return one.
The new keywords make do this for us! And is the only thing that word do.

Now we've to create our method's link in the proto property.

vehicleCreator.prototype.accelerate = function() {
  this.speed++;
}
Enter fullscreen mode Exit fullscreen mode

This code have the same function what we did with de Object.create().
Now we can create our objects with this way.

const vehicle1 = new vehicleCreator("Car", "Belina", 0);
const vehicle2 = new vehicleCreator("Car", "Monza", 0);
Enter fullscreen mode Exit fullscreen mode

All sweet, we facilitate so much the writing, performance and understanding of our code, but imagine that each new method we need to create a new link in prototype chain.

vehicleCreator.prototype.accelerate = function() {
  this.speed++;
}
vehicleCreator.prototype.decelerate = function() {
  this.speed--;
}
Enter fullscreen mode Exit fullscreen mode

We need to write our methods separeted of our object constructor. Sounds me unusual.

Javascript classes 🎩

From the ES2015 we had the insertion of syntactic sugar word class.
This emerges like a new development pattern and looks like with other programming languages.

However, the class keyword, under the hood, make exactly the same thing that we had above in the The "new" keyword topic.

With it, we can declare our methods together with our object constructor and we don't need to explicitly link with prototype chain.

class VehicleCreator {
  constructor(type, name, speed) {
    this.type = type;
    this.name = name;
    this.speed = speed;
  }
  accelerate() {
    this.speed++;
  }
  decelerate() {
    this.speed--;
  }
}
const vehicle1 = new vehicleCreator("Car", "Belina", 0);
vehicle1.accelerate();
Enter fullscreen mode Exit fullscreen mode

This code above, under the hood, make exactly the same thing of the code below, but is more elegant.

function vehicleCreator(type, name, speed) {
  this.type = type;
  this.name = name;
  this.speed = speed;
}
vehicleCreator.prototype.accelerate = function() {
  this.speed++;
}
vehicleCreator.prototype.decelerate = function() {
  this.speed--;
}
const vehicle1 = new vehicleCreator("Car", "Belina", 0);
vehicle1.accelerate();
Enter fullscreen mode Exit fullscreen mode

I'm end my article here. I've tried - and hope that I have succeeded - explain succinctly and direct about prototype chain, the diference between proto property and prototype (the big object), and the way what new and class keywords help us to object creation.

See you soon!👋

Understanding closures
Understanding Higher Order Function

Top comments (0)