Objects and Inheritance in Javascript

While some Javascript users may never have to know about prototypes or the object-oriented nature of the language, those coming from a traditional object-oriented programming language will poke around, and then find the inheritance model very odd. What confuses the matter more is the fact that different JS frameworks have their own convenience helpers for writing class-like code, the result of which is that

  1. there isn't one standard way to do it, and
  2. the underlying concepts are not as well known as they should be.

Prototype Inheritance

Prototype Inheritance is actually a very simple concept. Its essence is this:

  1. An object a can inherit from another object b. b is said to be a's prototype.
  2. a inherits all of b's properties, i.e. if b.x is 1, then a.x is also 1 automatically.
  3. a's own properties override those of b.

Let's see that in action. Let's say we have a John Smith and a Jane that inherits John.

> var john = {firstName: 'John', lastName: 'Smith'}
> var jane = {firstName: 'Jane'}
jane.__proto__ = john

at this point, we say john is jane's prototype, and jane inherits john's properties

> jane.lastName
"Smith"

jane's own property takes precedence though

> jane.firstName
"Jane"

Even if you add a property to john after the fact, jane will still inherit it - dynamically

> john.hair = 'brown'
> jane.hair
"brown"

Now, let's say Jane gets married - and therefore a new last name.

> jane.lastName = 'Doe'

Again, it will override john's lastName

> jane.lastName
"Doe"

but if we delete jane's lastName now

> delete jane.lastName

it will revert to being inherited from john

> jane.lastName
"Smith"

Now, john can inherit from other objects too. There can be any number of inheritances in this chain, which we call the prototype chain. In fact, john does has a prototype

> john.__proto__
Object { }

The result of john.__proto__ is rendered as Object { } in the Firebug console, but it represents the object Object.prototype - the ancestor of all objects.

So, that is prototype inheritance in a nutshell. There! That wasn't so bad, was it?

Well, actually... we can't use __proto__

I've got bad news...

__proto__ isn't supported in IE. In fact, __proto__ isn't in the ECMAScript spec and Mozilla is even thinking about dropping it in an upcoming version of Firefox.

That doesn't mean it doesn't exist though. Even on browsers where __proto__ is inaccessible, it is still there under the covers, and we still have to deal with it, just in a more roundabout way.

Classes and Inheritance

So, Javascript doesn't have classes.

Repeat after me: Javascript doesn't have classes.

Okay, then how do methods and inheritance work?

By using prototypes. Whereas in classic OO languages, methods hang off of classes, in Javascript, methods hang off of the prototype of the object, and, the prototype in question is tied to the constructor of the object.

In Javascript, functions double as constructors. You call a function as a constructor by using the new operator. For example if we create a Cat function

> function Cat(name){  // <- just a regular function
    this.name = name   // `this` refers to the object instance being made
}

a new object Cat.prototype gets created automatically.

> Cat.prototype
Cat { }

we can create a new instance of Cat using new

var garfield = new Cat('Garfield')   // makes a cat - acts as a constructor

now, Cat.prototype becomes the prototype of each object that's created via new Cat(), e.g. garfield.

> garfield.__proto__ === Cat.prototype
true // see? `Cat.prototype` is now garfield's prototype

Now we can hang a method off of Cat.prototype and it will be available for garfield.

> Cat.prototype.greet = function(){
    console.log('Meow, I am ' + this.name)
}
> garfield.greet()
"Meow, I am Garfield"

It will be available for other cats too

var felix = new Cat('Felix')
felix.greet()
"Meow, I am Felix"

So, in Javascript, methods live on the object's prototype.

Well actually, you can hang methods off of garfield itself too; it will override the one on Cat.prototype

> garfield.greet = function(){
    console.log("What's new?")
}
> garfield.greet()
"What's new?"

But it won't affect the other cats

> felix.greet()
"Meow, I am Felix"

So, a method can live directly on an object itself, a method can live on an object's prototype. A method can also live on any ancestor of an object, i.e. any part of the prototype chain. This is how inheritance is achieved.

To create a second level prototype chain, we first need to create another constructor. How about Animal?

> function Animal(){
}

Okay, now we need Cat.prototype's prototype to be an animal, so that a cat will inherit any of animal's methods as well. To do this, we set Cat.prototype to an instance of Animal.

> Cat.prototype = new Animal

One more thing: we also want to tell our new Cat.prototype that it is, in fact, an instance of Cat

> Cat.prototype.constructor = Cat    // letting `Cat.prototype` 
                                     // know that it is a cat

This is done mainly for housekeeping purposes, but necessary in general.

Now, since an animal inherits from Animal.prototype and Cat.prototype is an animal, all cats also inherit Animal.prototype indirectly. We can add a method to Animal.prototype and it will be available to any cat instance.

> Animal.prototype.breed = function(){
    console.log('Making a new animal!')
    return new this.constructor()
}
> var kitty = garfield.breed()
Making a new animal!

Cool, we just did inheritance!

Conclusion

Prototype inheritance in Javascript is pretty strange and will take some getting used to, but it is simple at heart. As long as you understand the underlying concepts you will be able to navigate OO Javascript code in the wild (both good and bad) with confidence.

blog comments powered by Disqus