Class-based vs prototype-based languages

You can read that title as Rest of the world vs Javascript.

Note: This is part of Javascript learning roadmap. Subscribe for more weekly web wisdom!

Why almost nobody really knows how prototypes work?

There are many aspects that makes Javascript stand out, but the prototype model is definitely the most important one. It brought into mainstream a new way of looking at the objects and relationships between them.

The prototype inheritance as a concept is intuitive and quite easy to understand. The main source of confusion, in my opinion, is the syntax. It is designed to be similar with the mainstream languages but it turns out to be pretty counterintuitive to the prototype model.

When a new developer, even with years of experience in programming, looks at a Javascript code and sees operators like newclass or constructor, it will be clear to him that object instantiation work the same way as he saw in all other languages he worked with.

However, those operators are nothing more than syntactic sugar in Javascript. They are designed to make the language more familiar for a new developer. Behind the scenes, things are slightly different.

Of course, you can live your live without caring what is under the hood if the behavior is the same. But it’s not really the case here. JS objects behave differently because they are different. Understanding how will help you predict their behavior without any problems.

In this article we will go through the conceptual differences between JS objects and the rest of the world. Let’s take it from the very basics and built up from there!


Class-based languages

A class based language (Java, C++, C#, Python, etc.) has a clear distinction between 2 concepts:

  • class – the interface which defines all the properties and methods
  • entities – instantiations of the class (objects)

An instance has the exact properties/methods declared in the class. You cannot modify them or add dynamically others. If you have an object, you can check it’s type (what class) and know exactly the properties it has and how it will be behave.

Any object in those languages belong to a class and only a class. Of course, those classes may inherit from others, but the instance has nothing to do with all of that.

I’m sure this sounds very familiar to you. Let’s move on to the good stuff!

Note: Some modern languages – like python and ruby – provide ways of dynamically change the attributes, but this is outside of the scope of this article.


Prototype-based languages

Prototype-based languages don’t have the concept of class or entities. They only have objects. Any object can specify its own properties at declaration or at runtime.

Let’s see an example:

// name property is added at declaration
var obj = {
    "name": "John"
}

//this would throw an error - sayName() is not declared yet
//obj.sayName();

// we add a new property at runtime
obj.sayName = function() {
    console.log("My name is " + this.name);
}
// and now it works
obj.sayName();

The method sayName() didn’t exist when the object was declared and was added at runtime. After adding it, we can use it with no problems. Check example here.


Prototype object

Since we don’t really have classes, inheritance in Javascript is possible due to prototype objects.

  • a prototype is an object that is used as a template from which we get the initial properties for a new object
  • any object can be associated as the prototype for another object and share the properties with that object
  • the object can override the inherited methods/properties inherited – this will not affect the prototype 
  • the prototype can change the attributes or add new ones – this will affect the object

So the process is pretty straightforward so far. We take one object and we create a copy of it. At that point, each object can move on with it’s life. The new object will have access to all the attributes of the prototype as long as is not override them (even the ones the prototype added after creation). The prototype doesn’t really care about it’s copy.  There is nothing that the copy can do to affect him. However, adding, deleting or changing any attribute will be visible in the copy as well.

This is the core of the prototype based concept. The inheritance is formed through prototype chain. Each object has its prototype. If not explicitly declared, the prototype is the global Object(). Let’s see an example:

Screen Shot 2017-09-13 at 11.10.10 AM

We start with the vehicle object which has is_used_for_transport property. From it, we create the Car object. Car will have the Vehicle as the prototype and 3 more attributes (terrestrial, wheels and engine). Tesla has car as prototype and 2 attributes – hp and engine – .

This is a prototype chain. Whenever an attribute is accessed from an object, the JS engine is looking for it in the chain.

Let’s assume we are accessing Tesla.is_used_for_transport property. First, the js Engine is looking for it in the Tesla attribute. Because it won’t find it, it will look further on Car and only then on vehicle. If the search will reach the end of the chain, it will return undefined.

For Tesla.engine, the search will stop at the first link in the chain – the Tesla object -. This is why, no matter what happens to the Car.engine, Tesla object has no more access to it.

If for example, we would type Tesla.toString(), the search will end at the end of the chain at Object. Global object has this method and therefore any object in JS has access to it.

This process is very similar to the scope chain lookup.

And that is it! The whole mystery surrounding the prototype-based languages can be summed up in few words:

  • no classes or instances, only objects
  • each object has a prototype – a prototype chain is form
  • the object has access to the attributes from the prototype chain, as long as it’s not overriding them

As you may noticed, I didn’t show any code yet in this chapter yer. I intentionally leave out JS syntax for now because it would only bring confusion. But now that we are aware of the concept, we can move on to decrypt the syntax.


Inheritance in Javascript

Every object in Javascript has a __proto__ attribute which points to its prototype. This attribute is the link in the prototype chain. It is used by JS engine in the lookup chain process. Even though the __proto__ property meant to be internal, most vendors allow access to it.  You can alter the chain by modifying it or even set it to null. It is strongly recommended to avoid altering it this way, but it’s important to know about its existence.

So to set a prototype to an object, all I have to to is create a new object and set the __proto__ attribute to the prototype I want to have. It will work!

However, in most cases, we would want to have a constructor function to ‘instantiate’ our objects.  It may look like instantiation, but it really is not. A constructor function is a normal function which creates a new object with the correct prototype if we use it alongside the operator new.


The constructor function

In the article about functions in depth, we saw that the function at its core is just another object, so it has a __proto__ as well. Besides that, functions had few predefined attributes, like arguments or prototype.  I explained then what arguments are used for, but now is time to talk about the last one.

The prototype is a property belonging to functions only. It is used to build the __proto__ when the function is used as a constructor with the new operator.

And that is it. The function’s prototype is simply an object. It’s prototype is the global Object() and, initially, it doesn’t have any other attributes except one named constructor which points back to the function itself.

When you create an object using that function, the __proto__ will point to the function’s prototype object (let’s name it FP – function prototype). Every object created with that function have in common the fact that they have FP as their prototype. So if you add an attribute in FP, it will automatically be visible in all objects.

In this aspect, the objects created with the same function behave as ‘instances’ of a class. They share the same attributes (the one you add in the FP). However, each of them can override (also named ‘shadow’) them at runtime.

So let’s see how the new operator actually work.


The new operator

Let’s start with an example:

function Car(name) {
    this.name = name
}
var tesla = new Car("Tesla");

tesla.name; // "Tesla"
tesla.__proto__; // the FT object
tesla.__proto__.constructor; // the function

When using the new operator to create an object from a function, the following things happen behind the scenes:

  • a generic object is created
  • the __proto__ of the object is set to Car.prototype (the FT object)
  • the Car function is called, with this representing the newly created object (so name will be added on object)

And that is it – 3 steps merged into one -, that is all new is doing.

Notice that I can create as many Car objects as I want and each of them has their own name attribute. But let’s say I would like to add a new attribute, wheels, for all objects already created.

Car.prototype.wheels = 4;

Now, whenever I access obj.wheels, the JS engine will find this property in the prototype chain and it will return it.

This strategy is commonly used for adding methods for objects. If I would add a method inside the constructor than every new object will have its own method (which is memory consuming). The better way is to add the methods in the prototype, so all objects would share the same method.

function Car(name) {
    this.name = name
}

var tesla = new Car("Tesla");

tesla.name; // "Tesla"
tesla.__proto__; // the FT object
tesla.__proto__.constructor; // the function

Car.prototype.horn = function() {
    console.log("hooorn");
}

tesla.horn(); // prints "hooorn"

And this is how the prototype chain looks for my object ‘tesla’:

Screen Shot 2017-09-13 at 2.00.44 PM

Please notice that name is the attribute of the object and the Car.prototype has the horn method. If I add a new object, let’s say ford = new Car(“Ford”), the chain will look like this:

Screen Shot 2017-09-13 at 2.05.06 PM.png

Both ford and tesla have access to the same horn() method and each one has its own name.

Screen Shot 2017-09-13 at 2.21.11 PM

It’s important to notice that the constructor/function role is to help creating the prototype chain between the new instance and FT object. After that, its role is over. Therefore, there is no direct link between the instance and the function/constructor.

Car function and tesla object have no direct reference to each other.


Classes in Javascript – Just more syntactic sugar

EcmaScript2015 introduced classes which were meant to make OOP in Javascript more familiar for developers. However, behind the scenes, the same thing is happening.

A Javascript class contains a constructor and other attributes.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  
  get area() {
    return this.calcArea();
  }

  calcArea() {
    return this.height * this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area);

This is equivalent to creating a constructor function and adding the attributes to it’s prototype.

A short side note:  Plain old Javascript is the only language spoke by all modern browsers. Every new feature, language (like Typescript) or modern framework, have to transcript their code into plain JS in order to be understand by the browser.  Understanding the basic of JS will help you learn much faster from the grown up.


Conclusion

I hope you have a much more clear picture of what prototypes are and how they work in JS. It took me a lot of struggle to comprehend all this, but trust me, it is worth it.

There are many ways and patterns for creating objects in Javascript, but as long as you understand the basics, you should have no problem picking the right one for your context.

My personal favorite is a pure prototypal way. Objects should inherit from objects with no middleman. EcmaScript2015 finally introduced a method of doing this – Object.create(obj) – You take an object and create a new one from it. The idea however is way older than EcmaScript2015 and it belongs to the man himself – Douglas Crockford -.

Check his article here.

Thank you for your time!

Please subscribe for more weekly web wisdom!

4 thoughts on “Class-based vs prototype-based languages

  1. JavaScript’s prototypal model isn’t as unique as we like to think. In Python and Ruby, for example, a class is itself an object (a building, not a blueprint), and inheritance is implemented using delegation, from instance to class object to superclass object and so on. Just like in JavaScript.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s