Skip to content

Understanding Javascript prototypes

JavaScript Prototypes Cover

Prototypes are simply mechanisms by which Javascript objects inherit properties from other JavaScript objects.

We already know how to create objects using object notation, and how to add or remove properties from such objects.

let object = {
  val1: "Hi",
  val2: true
}

object.val1 = "Hello!";
object.val2 = false;
object.valArray = [1, 2, true];

But what if we would need to create hundreds of different variations of object? Writing all this syntax would be tedious and would clutter and reduce our readability. So what is the workaround?

The new operator

JavaScript isn’t an object-oriented language and thus doesn’t have the encapsulation and inheritance capabilities that real OO languages have. Inheritance and encapsulation are helpful concepts that help us extend features of one object into another, or hide objects and certain properties. So we might naturally wonder, how can we implement a similar mechanism, in order to create multiple instances that have the same blueprint to reduce bloat, preserve code length and keep readability. Thankfully JavaScript provides the new operator that is applied to a constructor function, and which will create a newly allocated object by instantiating it using a constructor.

function Car() {
}

let car = Car();
console.log(car);

let carInstance = new Car();
console.log(carInstance);

If we invoke the car function as a regular function expression, using let car = Car(); nothing happens when we try to print out its contents, but when we use the constructor variation using new, the function gets a new object assigned to its prototype object. JavaScript has created a new object and assigned it an internal property that is not directly accessible ([[Prototype]] in the screenshot below). We also opened the constructor object and see properties associated with the constructor, such as the arguments, caller, length and name of the constructor.

Prototypes – the result of logging the carInstance object

We can extend that object as any other object by adding a random function to it:

Car.prototype.getSpeed = function() {
  console.log("Speed");
}

console.log(carInstance);

Now if we take a look at our prototype object we will see that the context of the function is changed.

Prototypes – the result of logging the carInstance object after adding a new prototype function

So what happened here? Every function created gets assigned a new prototype object. When we use a function as a constructor, the constructed object’s prototype is set as the function’s prototype. We extended the Car.prototype with the getSpeed function and when we try to access the getSpeed() property we would first check inside the function itself, and then the search will be delegated to the constructor of that function. This actually means that the getSpeed function is a property of the prototype and not of the instances.

Prototypes and instance properties

So, we create a function as a constructor using new and JS creates a new object instance which it assigns to the function we’ve just created. We can then use the exposed parameters, but in addition, can initialize new parameters using the this keyword.

function Car() {
  this.doors = 4;
  this.maxSpeed = 240;
  this.getSpeed = function() {
    return this.maxSpeed;
  }
}

Car.prototype.getSpeed = function() {
  return 120;
}

let carInstance = new Car();
console.log(carInstance);
console.log(carInstance.getSpeed());

The variables and methods above created with the help of this keyword are called instance properties. Why are they helpful? Because the instance properties will override the properties found in the prototypes. JS looks first in the local instance before moving out to the prototype, and thus the value we would get if we call getSpeed() is actually 240, because that is the value that is found constructor of the Car() method.

Prototypes - getting the result of the instance method instead of the prototypal method
Prototypes – getting the result of the instance method instead of the prototypal method
Prototypes – if a property can be found on the instance, there is no need to look for it in the constructor

There is no need to traverse the prototype chain, if we already created a property inside the constructor function, directly on the new carInstance instance. We would always try and pick local instances if they exist. Only if we don’t find any references inside the local instance we would look it up in the prototype chain. How can we utilize this behavior?

Often it is good enough to create methods on local instances. There is a caveat, though. Each instance gets its own version of the properties, and they share the same properties among themselves. This could become problematic when a large number of objects are created. Copying the same method among different instances consumes more memory. In cases such as these, we might decide to place objects methods on the function’s prototype, and then we can have a single method shared by all instances of an object.

Final Words

Knowing the ins-and-outs of prototypal inheritance is useful. If we use the prototype we can find the associated properties. We could also check the instance to know if it belongs to a certain constructor.

let carInstance = new Car();
console.log(carInstance instanceof Car)

The instanceof operator gives us a way to determine the instance and whether it was created by a certain constructor.

For more articles please click below, or check the blog.