Bits for hackers.

Transitioning to JavaScript From Ruby: Prototypes

Having gotten very comfortable with the beauty and elegance of Ruby over the past few months, making the transition to JavaScript was anything but painless. All of the small things such as having declare variables, remembering to insert semi-colons at the end of the line, and having to build out methods that felt like they should be part of the standard library (luckily underscore.js eased some of the pain here) made my life quite difficult. However, the hardest thing, I found, was getting into the JavaScript mindset of prototypes rather than classes and instances like we use in Ruby.

The main differences between these two approaches to object orientation are around distinctions between class objects and instance objects. In Ruby (and other similar, class-based languages), there is a clear distinction between instances and classes. However, in JavaScript, there is no such distinction - all objects are instances. These instances, called prototypes, just inherit the characteristics of other instances (called their constructor), which, in turn, is a prototype of another instance/constructor.

So let’s look at an illustrative view of what this really means:

This diagram shows a view of JavaScript’s prototype inheritance. Let’s start with Dog since that isn’t as abstract as Object or foo on either end of the chain. When the Dog object is created, it has a property called prototype; this property is just like any other property you might give to a dogs, such as numberOfLegs (another thing to adjust to when coming from Ruby is the convention of writing in camelcase!).

This property protoype of this instance of Animal called Dog lists all of the properties that the instance of Dog (e.g., a specific dog or maybe a type of dog, such as Bichon) will have once it is constructed. Now, once we create instances of Dog, otherwise known as prototypes of Dog, they will all have the properties that are defined in the prototype property (for example, if we say that when we construct an instance of Dog called Woofie, he should have all the properties and methods of Dog but also specific those of the prototype of dog, such as a name, “Woofie”).

Take a look at the diagram with the prototypes of Dog.

Now, when we look at the object of woofie the Dog, it will have a property called __proto__; this basically is a reference back to the prototype object that created it. So, two key things to remember:

  • The “`__proto__“` property is part of any object that is the prototype of another object (it was created using the prototype property of another object). This property holds the reference and points to the “`prototype“` that created it.
  • The “`prototype“` property is part of the object that is being used to construct the prototype. The information contained in this property is what is used as a template to create the prototype object (e.g., “`woofie“`). This property also holds a reference of “`constructor“` that points back to the object that created it. For example, the protoype of “`woofie“` (remember, the prototype of “`Dog“`) would hold a reference to “`Dog“` as its “`constructor“`.

Now, the object Dog was created the same way from the prototype of Animal as we created woofie from the Dog constructor. We can create an entire chain of prototypes in this way to create this prototype chain. At some point, we get to the instance of Object, which is the first object in Javascript. This means that it is not a prototype of anything - its __proto__ property holds a reference to null rather than to another prototype.

Enough talking, let’s look at some code! Let’s start by looking at one end of the chain at the object “foo”.

1
foo.constructor //this will result in Foo as you go up the chain

As you can see here, when you look at the constructor of the foo instance, you see that it is Foo since it was the prototype of Foo that created the instance of foo. Keep in mind that the constructor property here isn’t actually part of the foo object; it is part of the prototype object that the `__proto__ property of foo points to. However, as we mentioned, everything in Javascript is an instance of another object (except for Object which is not an instance of anything), so, you can call:

1
Foo.prototype.__proto__.constructor //this results in Bichon

In the example above, you basically traverse back to the object that created Foo by:

  1. Calling the prototype property of Foo
  2. Calling reference (“`__proto___“`) to the prototype that made Foo
  3. Looking at the constructor of that prototype, which is Bichon

Now, just for fun, lets take a look how we can traverse from the foo object to the Object object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
foo.__proto__ //returns Foo.prototype

//both examples below return the same thing and are analagous to one another
Foo.prototype.__proto // returns the Bichon.prototype
foo.__proto__.__proto___ // returns the Bichon.prototype

//both examples below return the same thing and are analagous to one another
Bichon.prototype.__proto__ //returns Dog.prototype
foo.__proto__.__proto___.__proto___ //returns Dog.prototype

//both examples below return the same thing and are analagous to one another
Dog.prototype.__proto__ //returns Animal.prototype
foo.__proto__.__proto___.__proto___.__proto___ //returns Animal.prototype

//both examples below return the same thing and are analagous to one another
Animal.prototype.__proto__.constructor //returns Object
foo.__proto__.__proto___.__proto___.__proto___.__proto___.constructor //returns Object

For those who may be more visually inclined, here’s an illustration of this code that I put together of this traversal tht we’re doing:

Finally, let’s make this example a little more realistic and show how it relates to Javascript code. Let’s create a new Dog constructor:

1
2
3
4
5
6
7
8
function Dog(name){
  // This is a Dog constructor that will be used to create instances of a Dog, such as Woofie.
  // You use this space to put properties of the dog instance in here.
  this.name
  // REMEMBER: one of the properties of this constructor is the prototype object which holds a reference to the constructor and the
  // __proto__ that points to the prototype that created the Dog object.
  // The prototype is a property of this constructor JUST LIKE name is.
}

Now, let’s add some other behaviors the instances of Dog using the prototype property. You call this just like you would call any other property of Dog.

1
2
3
// Again, here, you are calling the prototype property just like you would any other property and then adding another property nested
// within that called "bark".
Dog.prototype.bark = function(){...}

Finally, let’s create Woofie! We use the Dog constructor to create Woofie.

1
var woofie = new Dog("Woofie");

Since Woofie has a __proto__ reference to the Dog prototype, it will also, naturally inherit the ability to bark that we gave all the prototypes of Dog.

Understanding the concepts above really helped me to better understand the fundamentals of Javascript. For further reading on this topic, check out these great resources: