Zeroes and Ones

Object-Oriented Programming 101. Prototypes

Inheritance exists to make our lives easier. If we are modeling let's say, a person, there are some properties that we might want to consider, for example name, age, and whether the person has superpowers or not (well maybe this last point is not super relevant in most scenarios unless you are Magneto).

Now we want to model a student, and teacher, but soon enough duplicating all of that information feels tedious. Both student and teacher objects would be persons, so Inheritance allows us to build them as children of the person object. If I later decide to add "favorite movie" there (Lord of The Rings of course), this will be inherited by student and teacher objects.

Classic vs Prototypical Inheritance

Programming languages like Java or C++ implement classic inheritance. When we talk about "Person" or "Student" in that context, we are talking about a class, that is the actual blueprint for the objects that are generated by it. That is !how it works in Javascript (by ! I mean not, just mixing programming syntax with regular english, come on, keep up).

Javascript has prototypical inheritance, a prototype is essentially the parent of another object (Yes, Javascript doesn't really have classes, the ES6 class keyword is just syntactic sugar for the "good ol' constructor function"). Every object in Javascript (besides one) has a parent object from which it inherits from. We can easily check this prototype property using "dunder proto", that is: myObject.__proto__.

Another way to check the prototype of my object? Glad you asked. Object.getPrototypeOf(nameOfObject); If we check what gets returned, we can see a property called constructor that is the constructor function that created the object. If I run that code for an array, the constructor will be Array(). As said before the only object that doesn't have any prototype is the "base object" that is, the prototype of a regular object (by regular I mean one created with object literals for example, not with another constructor function).

Constructor and Prototypes

One cool thing to notice is that all objects created with the same constructor function, will share a common prototype. If I do something like const felipe = new Person('Felipe') and const carla = new Person('Carla') both objects will share the same prototype object, so basically felipe.__proto__ === carla.__proto__, another way of knowing the prototype is by accessing it through the constructor function like Person.prototype.

OK, cool story bro, but why does this matter? Well for once, imagine you are defining the Person constructor function, and you want to define a method. The first intuition would be to define it inside the function like:

function Person(name) {
  this.name = name
  this.shoutGreeting = function () {
    return "HELLO!!!"
  }
}

That is ok, and there might be good reasons to do so, but most times there is a better way:

Person.prototype.shoutGreeting = function () {
  return "HELLO!!!"
}

(Could we have done that with __proto__? Technically yes, but is not recommended)

This is better, why? Because with the constructor function we are actually defining the same method over and over again for each of the instances we create, using the second approach is cleaner, JS will look for the shoutGreeting property in the instance, since it won't be there, it will look in the prototype, it will find it, and call it. Need to change the method? Easy, we just do it in the prototype and the change will propagate to all the children.

We can say then, that name is an instance property member, it will be direct from each instance, where as shoutGreeting will be a prototype member, so accessible for each instance, but not defined in them but in their prototype.

Important thing to mention is when I use something like Object.keys() to check properties of an object, I will only get the instance members, but if I use the for (let key in object) syntax, I will get both.

Developed by Cristobal Heiss