Header Ads Widget

Class-based vs Prototype-based Programming using JavaScript


Douglas Crockford described JavaScript as the world's most misunderstood language. Like many developers, when I started learning JavaScript, to me also, this was not a "proper" language. But I quickly understood that JavaScript comes with a rich system of object-oriented programming.

There are many concepts that makes JavaScript unique, but prototype model is definitely the most important one and it creates many confusions. So having a clear understanding of prototype model is very important. And in this post, I would like to explain the concept of prototype based programming in JavaScript.

The prototype based programming is a very easy to understand concept, but the source of confusion is the syntax. When a new developer looks at the JavaScript code and sees new, class or constructor it makes them think that object instantiation works the same way as other languages. But in reality, they are nothing more than syntactic sugar in JavaScript. Behind the scenes, things are working in different way. So it is very important to have a clear understanding how things works which will help you to predict their behavior.

Class Based Language

A class based language (Java, C++, C# etc.) has two important concepts:
  • class - the interface which defines the properties and methods.
  • entities - instantiations of the class (objects)
 An instance is exact same copy of the class, you cannot dynamically add/remove properties/methods from the instance as declared in the class.  You can definitely override them, but then it comes to inheritance. In this model, one instance belongs to one class only. Of course, those classes can also inherit from others. This is how Class Bases language works.

Note - There are some programming language like Python and Ruby which provide the way of dynamically change the attributes.

Prototype Based Language

Here we don't have the concept of classes or entities. It's only objects. An object can specify it's properties either at declaration or at runtime.
Let's see the below example:
//Property name at declaration
var student = {
  "name":"Sudipta Deb"
}

//This will throw an error as the function is not yet defined.
student.sayFullName(); 

//Let's add the function as runtime
student.sayFullName = function(){
  console.log('FullName: ' + this.name);
}

//This time it will print the fullname
student.sayFullName();

Check example here. Initially the method sayFullName() was declared, but it was added dynamically later.

Understand Prototype Object

Without classes in JavaScript, inheritance is only possible due to Prototype objects. Here are few important pointers to understand Prototype object.
  • A prototype is an object which is used as template from which all new objects will get their properties.
  • Any object can be used as a prototype for another object and share the properties with that object.
  • The object can override the inherited methods/properties, but it will not impact the prototype.
  • The prototype can change it's methods/properties or add new ones, which will impact the objects which was created from the same prototype.
So basically we create copies from one object and then all copies will move with their own life. The new object will have access to all the methods/properties of the prototype as long as they are not overridden. Even if there is any method/property being added to the prototype class after the object creation, object will still have access to those newly added method/property. Any changes in the object will not impact prototype, but any change in prototype will impact all objects. This is the core of prototype based programming. Let's understand this below example.

Vehicle Example


Let's create the hierarchy in JavaScript. 
First the Vehicle definition.
 function Vehicle(){  
  this.manufacturers = '';  
  this.year = '';  
 }  

Let's the create the two sub classes for Vehicle i.e. Bus and Car as below

//Definition of Vehicle Object
function Vehicle(){  
  this.manufacturers = '';  
  this.year = '';  
} 

//Definition of Bus Object
function Bus(){
  Vehicle.call(this);
  this.routeNumber ='';
}
Bus.prototype = Object.create(Vehicle);
Bus.prototype.constructor = Bus;

//Definition of Car Object
function Car(){
  Vehicle.call(this);
  this.type = '';
}
Car.prototype = Object.create(Vehicle);
Car.prototype.constructor = Car;

Every object in JavaScript has a __proto__ attribute which basically points to its prototype. This attribute creates the link in prototype chain. In Javascript, you add a prototypical instance as the value of the prototype property of the construction function and then override the prototype.constructor to the construction function.

So prototype property holds the parent object. When an object is created through Object.create, the passed object is meant to be the prototype of the new object. 

So now let's extend the example above and create GasolinCar and ElectricCar. 

//Definition of Vehicle Object
function Vehicle(){  
  this.manufacturers = '';  
  this.year = '';  
} 

//Definition of Bus Object
function Bus(){
  Vehicle.call(this);
  this.routeNumber ='';
}
Bus.prototype = Object.create(Vehicle);
Bus.prototype.constructor = Bus;

//Definition of Car Object
function Car(){
  Vehicle.call(this);
  this.type = '';
}
Car.prototype = Object.create(Vehicle);
Car.prototype.constructor = Car;

//Definition of Gasolin Car
function GasolinCar(){
  Car.call(this);
  this.mileage = '';
}
GasolinCar.prototype = Object.create(Car);
GasolinCar.prototype.constructor = GasolinCar;

//Definition of Electric Car
function ElectricCar(){
  Car.call(this);
  this.batteryCapacity = '';
}
ElectricCar.prototype = Object.create(Car);
ElectricCar.prototype.constructor = ElectricCar;

The new Operator

Let's start with the below example:

//Let's create new objects now
var teslaGasolinCar = new GasolinCar();
teslaGasolinCar.mileage = 12;
teslaGasolinCar.type = 'Car';
teslaGasolinCar.manufacturers = "Tesla";
teslaGasolinCar.year = 2018;
console.log('Mileage:' + teslaGasolinCar.mileage);
console.log('Type:' + teslaGasolinCar.type);
console.log('Manufacturer:' + teslaGasolinCar.manufacturers);
console.log('Year: ' + teslaGasolinCar.year);
console.log('__proto__: ');
console.log(teslaGasolinCar.__proto__);
console.log('prototype: ');
onsole.log(teslaGasolinCar.prototype);

When using the new operator to create object from a function, below things happen behind the scene:

  • A generic object is created.
  • The __proto__ is set to GasolinCar.prototype
  • Finally GasolinCar function is called, with this representing the newly created object
Now when the above example is executed, here is the output in the console. 

Note - 
__proto__ is holding the GasolinCar.prototype, and prototype is holding Car object. This is how the inheritance is created in JavaScript.

It's important to understand that the constructor/function role is to help creating prototype chain between the newly created instance and it's parent object. After that, it is having no other job. Therefore, there is no direct link between the instance and the function/constructor. 

teslaGasolinCar instance and GasolinCar function have no direct reference to each other.


You can run this example here.





Create Multiple Objects with new Operator

You can create multiple objects with new operator, but each object will get their own property.
In the below example, I have created two objects of same type as -

//Definition of Vehicle Object
function Vehicle(){  
  this.manufacturers = '';  
  this.year = '';  
} 

//Definition of Bus Object
function Bus(){
  Vehicle.call(this);
  this.routeNumber ='';
}
Bus.prototype = Object.create(Vehicle);
Bus.prototype.constructor = Bus;

//Definition of Car Object
function Car(){
  Vehicle.call(this);
  this.type = '';
}
Car.prototype = Object.create(Vehicle);
Car.prototype.constructor = Car;

//Definition of Gasolin Car
function GasolinCar(){
  Car.call(this);
  this.mileage = '';
}
GasolinCar.prototype = Object.create(Car);
GasolinCar.prototype.constructor = GasolinCar;

//Definition of Electric Car
function ElectricCar(){
  Car.call(this);
  this.batteryCapacity = '';
}
ElectricCar.prototype = Object.create(Car);
ElectricCar.prototype.constructor = ElectricCar;

//Let's create Tesla objects
var teslaGasolinCar = new GasolinCar();
teslaGasolinCar.mileage = 12;
teslaGasolinCar.type = 'Car';
teslaGasolinCar.manufacturers = "Tesla";
teslaGasolinCar.year = 2018;
console.log('Printing Tesla Car:==>');
console.log('Mileage:' + teslaGasolinCar.mileage);
console.log('Type:' + teslaGasolinCar.type);
console.log('Manufacturer:' + teslaGasolinCar.manufacturers);
console.log('Year: ' + teslaGasolinCar.year);

//Let's create Ford objects
var fordGasolinCar = new GasolinCar();
fordGasolinCar.mileage = 11;
fordGasolinCar.type = 'Car';
fordGasolinCar.manufacturers = "Ford";
fordGasolinCar.year = 2019;
console.log('Printing Ford Car:==>');
console.log('Mileage:' + fordGasolinCar.mileage);
console.log('Type:' + fordGasolinCar.type);
console.log('Manufacturer:' + fordGasolinCar.manufacturers);
console.log('Year: ' + fordGasolinCar.year);

You can run this example here.



Conclusion

I hope you have a much clear understanding of how prototype based programming is used in JavaScript. In JavaScript, you can create objects in multiple ways, but as long as you understand the basic, it will be easy for you to understand and pick the correct way.

Thank you for your time and please feel free to provide your feedback in comments below.

Further Reading


Post a Comment

1 Comments

  1. This comment has been removed by a blog administrator.

    ReplyDelete