Mastering JavaScript's Prototype

With CoffeeScript gaining steam I think now is a good time to learn about some Object Oriented Programming in JavaScript. The reason being that CoffeeScript sugarcoats a lot of JavaScript’s OOP, and, like learning Rails before Ruby, the magical code can make you end up with an incomplete picture of how the underlying language actually works.

Coming from Ruby, I’m used to concepts such as classes, instances of classes, methods, inheritance e.t.c. One scary thing about JavaScript, is that it seemingly has none of these. Instead we have functions, objects, constructors and prototypes. Luckily, these things are not too far removed from what you’re used to, once you see what’s actually happening. Firstly, you need to understand that JavaScript is a “prototypal language”:

Prototype-based programming is a style of object-oriented programming in which classes are not present, and behavior reuse (known as inheritance in class-based languages) is performed via a process of cloning existing objects that serve as prototypes.

- Wikipedia

Lets try to create a Person class that pumps out people objects with a “name” property. We will do this in Ruby and also the equivalent in JavaScript.

So in Ruby, when you create an object, it will look at the class of the object and run the initialize method. This method will setup the object and handle any parameters you pass in.


class Person
  def initialize(name)
    self.name = name
  end
end

If we run this we will find it won’t work since there is no setter method for the “name” instance variable. This variable hiding/protection is known as encapsulation in object oriented programming. To make a variable accessible to the outside world, we need to create a method to set it:


class Person
  def initialize(name)
    self.name = name
  end

  def name=(val)
    @name = val
  end
end

So, why do you care about this Ruby stuff? Well, in my opinion this is a good example of OOP, and something to compare against. In JavaScript, things work a little differently. Firstly, like Ruby, everything in JavaScript is an object. However, in JavaScript objects are simply sets of key-value pairs. Key’s are always strings and values can be strings, numbers, booleans, and objects (including arrays and functions). If the value is a function, then this is the equivalent of a method.


var obj = {}

// is the same as:

var obj = new Object;

var obj2 = {
  key1: "value1",
  key2: "value2",
  aMethod: function(){}
}

So to create the equivalent of a Person class in JavaScript what would we do? We know that JavaScript uses prototypes instead of classes. So perhaps we need to create a “Person” prototype that other objects can clone from? Well we could try this for starters:


var Person = {
  initialize: function(val){
    this.name = val;
  }
}

Seems logical, but when we try and create a new Person, we get an error:


var p = new Person;
TypeError: object is not a function

Here we have made a bit of a fundamental error. We have tried to create the prototype itself, when really we should be focusing on the constructor, which is kind of like the initialize method in Ruby. So var Person is going to be a constructor function, not a prototype that new objects clone from.

Basically when you go something like “var me = new Person”, you are sending the “me” object through the Person constructor function. But while it’s tumbling through the constructor it’s not called “me”, but rather is called “this”. So the Person function should look more like this:


var Person = function(name){
  this.name = name;
}

So when we go “var me = new Person(“Matt”)”, this is what is happening:


var me = new Person("Matt");

// ... is equivalent to this: (not real code)
var Person = function("Matt"){
  me.name = "Matt";
}

me;
Person

me.name;
"Matt"

Any function can be a constructor, which is why they are usually named in camel case (capitalised) to show that they are different to your average function.

With a Person constructor pumping out Person objects, what if you want to set a new property to all these objects? For example, we want to add a last_name property.

We could edit the constructor:


var Person = function(name){
  this.name = name;
  this.last_name = "Platts"
}

…which would work for any new Person objects that are created, but what about the ones already initialized? This is where the prototype comes to the rescue. Every object in JavaScript has a prototype object, which technically should be hidden to all except the constructor of the object. I say “technically” because the latest browsers allow you to access any objects prototype.

Anyway, one thing to realise is that we aren’t going to be modifying all those pre-made People objects directly – they will stay the same. Just their prototype will be changed. And this will work because when we call “me.last_name”, the JavaScript interpreter will first look in the “me” object for the “last_name” property, and if it doesn’t find it, it will then look inside its prototype (which is where we are going to put the “last_name” property).

So how to access the “me” object’s prototype? There are several ways to do it, however the easiest way is through the Person constructor function:


Person.prototype.last_name = "Platts"

Now this can be confusing. It is important to understand that Person.prototype is not actually referencing Person’s prototype, but rather the prototype of all the Person objects – the ones that were constructed as “new Person” (like the “me” object above). If we went “me.prototype”, we would get an error, because “me” is not a constructor and therefore can’t have any of it’s own objects. So the “.prototype” method is kind of misleading in that it’s only usable by constructors to get access to the prototype of any objects it creates.

So let’s try and get this straight:

  • any function can be a constructor
  • constructor.prototype gives you the prototype of any objects created by the constructor, not the prototype of the constructor itself
  • if you try to access a property on an object, JavaScript will check the object, then its prototype, then its prototypes prototype, and so on up the prototype chain

Also note that every object has access to it’s constructor function:


me.constructor
function (name){
  this.name = name;
  this.last_name = "Platts"
}

Meaning you could also go me.constructor.prototype, however this is not recommended due to its unreliability in more complex cases. To properly access an objects prototype modern browsers have provided the __proto__ property. So me.__proto__ is the equivalent of Person.prototype.

This concludes this article – thanks for reading. Hopefully you have learnt something.

Note: to complete your understanding I would go to coffeescript.org, click “Try CoffeeScript” and then in the console to the left type “class Person”. Watch how CoffeeScript creates the Person class.

Resources:
http://javascript.crockford.com/inheritance.html
http://www.crockford.com/javascript/private.html
http://ejohn.org/blog/simple-javascript-inheritance/

Guest Post by Matt Platts


[author_bio]

Matt Platts

2 Comments

  1. Your statement at the beginning of “If we run this we will find it won’t work since there is no setter method for the “name” instance variable.” is incorrect. The code will execute cleanly, assigning the value of name to the @name instance variable. You’d only have a problem if you tried doing “self.name = name”

This post currently has 2 responses. What do you think?

You can use basic HTML when posting code, please turn all < characters into &lt; or > into &gt;
If the code is multi-line, use <pre><code></code></pre>

Leave a Reply

Your email address will not be published. Required fields are marked *

Current day month ye@r *