Javascript Classes — Under The Hood

Javascript classes are nothing but a syntactic sugar over existing prototype based inheritance and constructor functions. In order to understand the idea behind JS classes we need to understand the constructor functions, prototype and other related concepts.


1. Constructor Functions

Since Javascript is a functional programming language where everything is just a function, in order to have a class like (creating a blueprint for the objects to be created) functionality in javascript, constructor functions are used, lets see how constructor functions work:

function Vehicle(make, model, color) {
this.make = make,
this.model = model,
this.color = color,
this.getName = function () {
return this.make + " " + this.model;
}
}

The function above provides almost similar functionality as that of a Java class, in order to create an object of type Vehicle, we do following:

let car = new Vehicle("Toyota", "Corolla", "Black");
let car2 = new Vehicle("Honda", "Civic", "White");

This is perfect. Ain’t it? now, we can create as many objects of type Vehicle as we want by just writing one line code.

But wait, there are a few problems with this technique.

When we write new Vehicle(), what Javascript engine does under the hood is that it makes a copy of our Vehicle constructor function for each of our objects, each and every property and method is copied to the new instance of the Vehicle, that’s okay, what is the problem in this?!

The problem here is that we don’t want the member functions (methods) of our constructor function to be repeated in every object. That is redundant. Isn’t it? Another problem is that we can’t add a new property or method to an existing object like this:

car2.year = "2012"

To add this year property you will have to add it to the constructor function itself:

function Vehicle(make, model, color, year) {
this.make = make,
this.model = model,
this.color = color,
this.year = year,
this.getName = function () {
return this.make + " " + this.model;
}
}

2. Prototype

Whenever a new function is created in javascript, Javascript engine by default adds a prototype property to it, this property is an object and we call it “prototype object”. By default this prototype object has a constructor property which points back to our function, and another property __proto__ which is an object, have a look at the following:

The __proto__ property is called dunder proto, and it points to the prototype property of our constructor function.

“word dunder comes from python, where variable names bracketed with double underscores are called dunder properties”

Whenever a new instance of the constructor function is created this property is also copied to the instance along with other properties and methods:

Now, this prototype object can be used to add new properties and methods to the constructor function using the following syntax and they will be available to all the instances of the constructor function:

car.prototype.year = "2016";

Prototype is cool, but there are a few things you need to be careful while using prototype approach. Prototype properties and methods are shared between all the instances of the constructor function, but when any one of the instances of a constructor function makes any change in any primitive property, it will only be reflected in that instance and not among all the instances:

Another thing is that reference type properties are always shared among all the instances, for example, a property of type array, if modified by one instance of the constructor function will be modified for all the instances:

There is a very interesting and more detailed article on prototype here.


3. Classes

We understand the constructor functions and prototype, now its easy to understand the class, why? because javascript classes are nothing but just a new way of writing the constructor functions by utilizing the power of prototype, lets see an example for this:

class Vehicle {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}

getName() {
return this.make + " " + this.model;
}
}

And in order to create a new instance of class Vehicle, we do this:

let car = new Vehicle("Toyota", "Corolla", "Black");

So if you compare this with what we did in the beginning while explaining the constructor functions, it is very similar.

By writing the above code, we have actually created a variable named Vehicle which references to function constructor defined in the class, also we have added a method to the prototype of the variable Vehicle, same as bellow:

function Vehicle(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}

Vehicle.prototype.getName()= function () {
return this.make + " " + this.model;
}

let car = new Vehicle("Toyota", "Corolla", "Black");

So, this proves that class is a new way of doing the constructor functions. Yes, but there are few new things introduced and some rules set in order to make it more like actual classes.

1. constructor requires new keyword to work. It means constructor will only be called when we do following.

let car = new Vehicle("Toyota", "Corolla", "Black");

But in constructor functions we could actually do this:

This happens if you try to instantiate a class variable like above:

2. Class methods are non-enumerable. In javascript each property of an object has enumerable flag, which defines its availability for some operations to be performed on that property. A class sets this flag to false for all the methods defined on its prototype.

3. If you don’t add a constructor to a class, a default empty constructor will be added automatically. Same as the following:

constructor() {}

4. Code inside a class is always in strict mode, this helps to write error free code, by throwing errors, on mis typings, or syntactical errors done while writing code, or even removing some code by mistake, which is referenced from somewhere else.

5. Class declarations are not hoisted. Hoisting in javascript is behavior in which all the declarations are automatically moved on top of the current scope, this behavior actually lets you use a variable or a function before its declared.

So Class declarations are not hoisted means, you can’t use a class before it is declared, it will return not defined error, see below:

This works:

But this doesn’t:

6. Classes doesn’t allow the property value assignments like constructor functions or object literals. You can only have functions or getters / setters. So no property:value assignments directly in the class.


4. Class Features

1. Constructor

Constructor is special function in the class declaration, which defines a function, that represents the class itself. When you new up a class instance, the constructor is automatically called.

let car = new Vehicle("Honda", "Accord", "Purple");

A constructor can use super keyword to call the constructor of the class its extending from.

A class can not have more than 1 constructor function.

2. Static Methods

Static methods are functions on class itself, and not on its prototype, unlike the other methods in the class, which are defined on its prototype.

Static methods are declared using static keyword, and are mostly used to create utility functions. They are called without creating the instance of the class. See an example below.

class Vehicle {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}

getName() {
return this.make + " " + this.model;
}

static getColor(v) {
return v.color;
}
}

let car = new Vehicle("Honda", "Accord", "Purple");

Vehicle.getColor(car); // "purple"

Remember, static methods can not be called from the class instance.

3. Getters/Setters

Class can also have getter/setters to get the property value and or to set the property values. Something like below.

class Vehicle {
constructor(model) {
this.model = model;
}

get model() {
return this._model;
}

set model(value) {
this._model = value;
}
}

Under the hood, getters/setters are defined on the class prototype.

4. Subclassing

Subclassing is a way you can implement inheritance in Javascript classes, keyword extends is used to create a child class of a class.

Lets take an example:

class Vehicle {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}

getName() {
return this.make + " " + this.model;
}
}

class Car extends Vehicle{
getName(){
return this.make + " " + this.model +" in child class.";
}
}

let car = new Car("Honda", "Accord", "Purple");

car.getName(); // "Honda Accord in child class."

You can see when calling the getName() function, the function from the child class was called.

Sometimes we need to call function of the base class. We use super keyword in order to call the base class methods from within the methods of the child class.

Change the getName() function in the child class to something like:

class Car extends Vehicle{
getName(){
return super.getName() +" - called base class function from child class.";
}
}

Now, if you call getName() from the instance it should return the result as below:

So, we tried to explain the idea behind the Javascript classes and in the end we learnt about some of the features of classes.

I surely have missed much of the details on the topic, you can continue reading through following awesome articles written by Javascript geeks out there, not only this, there are tons of other articles available on the internet, that can help you grab the idea behind the topic.

References:

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
  2. http://2ality.com/2015/02/es6-classes-final.html
  3. https://javascript.info/class
  4. https://thejsguy.com/tutorials/javascript-constructor-functions-and-classes
  5. https://www.phpied.com/3-ways-to-define-a-javascript-class/