So, how do you make classes and subclasses in Javascript? Lets start by reviewing how to make plain old classes in Javascript. If you’re using pseudoclassical instantiation, like most people do, classes in Javascript are created through object constructor functions. When we invoke the object constructor, it returns an object that is pre-packaged with all of the properties we’d like it to have. See the example below. Animal is an object constructor function, and we are using it to make objects that all start with the same specific properties. All of the objects we create with this constructor function will fall under the Animal class:

var Animal = function(type){
    this.type = type;
}

Animal.prototype.sayType = function(){
    console.log("I am a " + this.type);
}

var pig = new Animal("pig");
pig.type    //returns "pig"
pig.sayType    //logs "I am a pig"

In the above example, we created an Animal class, and then we created a pig, which is an object that now falls under the Animal class, and thus shares the Animal class’s properties and prototype properties. Any object we create using the new Animal() constructor will have Animal’s properties (those being the ones we specify within the body of the constructor function - this.type - and the ones we specify within the constructor’s prototype property - prototype.sayType). Let me take a moment here to point out that there is a very specific reason why the pig gets the prototype properties of Animal. It is not magic - it is because when we invoke the Animal constructor prefaced by the ‘new’ keyword, before running the code inside the constructor function, Javascript actually creates a ghost object that equals a reference to the function’s prototype property (which, on all functions, as you may or may not know, starts out as an empty object. But in this case since we defined Animal.prototype.sayType to equal a function, it now looks like this: {sayType: funciton(){…etc.}} ). Javascript then sets all instances of the ‘this’ variable inside of the constructor body (in this case, this.type), to point to the ghost object (so, in this case, the ghost object will now look like {sayType: xxx, type: xxx} ). Javscript then returns the ghost object. That is how, in the code above, pig ends up with both the ‘type’ and ‘sayType’* properties. *As I will explain again later, one caveat worth noting here is that pig appears to have the property ‘sayType’, but it really only delegates to it, meaning that it can use and reference it, but if you type Object.hasOwnProperty(‘sayType’) you will get false. sayType actually only resides as a property within Animal.prototype, and pig only references it when needed. Prototype is worthy of its own blog post, and I may write one later, which you can reference if you are confused.

Now, on to Subclasses. If we want to continue using the pseudoclassical style to make a Subclass of Animal that will share all of Animal’s properties and also have its own unique properties, this will require a little bit of javascript ingenuity in order to do it properly.

As an example, let’s make a subclass called Dog. Everytime we call new Dog() it should create a new dog object. Because Dog is a subclass of Animal, the resulting dog will have all of Animal’s properties (this.type), and will have delegation access to all of Animal’s prototype properties (as i described earlier, this means that it can use/reference Animal’s prototype properties, but won’t actually contain them). Dog will have a couple of its own properties as well, which will be unique to all Dogs. That was confusing…lets recap:

Creating the Dog subclass will require 4 steps:

1) Define the Dog constructor, and define unique dog properties using the ‘this’ variable.

var Dog = function(breed){
    this.breed = breed;
}
var dog = new Dog('bulldog');

//At this point, dog would look like this: {breed: 'bulldog'}

2) Right now, our Dog constructor doesn’t yet contain any of Animal’s properties, so in order to pass them to Dog, we will call the Animal constructor within the Dog constructor’s function body.

var Dog = function(breed){
    Animal.call(this,'dog');
    this.breed = breed;
}
var dog = new Dog('bulldog');

//Calling Animal effectively adds this line of code to our Dog function: this.type = 'dog'
//At this point, dog looks like this: {breed: 'bulldog', type: 'dog'}
//Advanced topic: If you’re curious, the reason why we use .call instead of just invoking Animal() is because the ‘this’ variable within Animal would point to the global scope if we just called Animal as a free standing function. By using .call, and passing in ‘this’ we specify that the ‘this’ variable in the context of ‘this.type’, should point to the ‘this’ variable within the context of Dog (which is effectively the same ‘this’ variable that ‘this.breed’ is pointing to).

3) Dog.prototype is currently an empty { }. We must set Dog’s prototype to delegate to Animal’s prototype, so that our new doggy gets access to all of Animal’s prototype properties (in this case, ‘sayType’).

var Dog = function(){
    Animal.call(this,'dog');
    this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
var dog = new Dog('bulldog')

//Object.create() is a magic method that creates an object that REFERENCES all of the properties of the passed in argument object. So, in this case, it creates an object that delegates to Animal.prototype. We are then setting Dog.prototype to be that object. Dog.prototype now effectively delegates to Animal.prototype, just the same as pig delegates to Animal.prototype.
//At this point, dog looks like this: 
{
    breed: 'bulldog', 
    type: 'dog', 
    sayType: function(){console.log(this.type)}
}

4) If we want the Dog subclass to include any of its own prototype properties, we can now include them by adding methods to Dog.prototype.

var Dog = function(){
    Animal.call(this,'dog');
    this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.sayBreed = function(){
    console.log("Woof woof, I am a " + this.breed);
}
var dog = new Dog('bulldog');

//At this point, dog looks like this:
{
    breed: 'bulldog',
    type: 'dog',
    sayType: function(){console.log("I am a " + this.type)},
    sayBreed: function(){console.log("Woof woof, I am a " + this.breed)}
}

5) Finally, if you want to be really neat and careful, you may want to redefine the Dog.prototype.constructor property. That’s right…every Function.prototype comes with a hidden constructor property and it points to the constructor function itself as a value. Since our Dog subclass delegates to the Animal.prototype, our Dog.prototype.constructor property currently equals Animal, but we want it to equal Dog. It is an easy fix, just add this line of code:

Dog.prototype.constructor = Dog;

6) All done! We now have a Dog subclass that we can use to make all types of dogs.

var dog1 = new Dog('Collie');
var dog2 = new Dog('Poodle');
var dog3 = new Dog('Akita');
comments powered by Disqus