Tag Archives: Javascript classes

I'm working on a new pet project - called Risk... in the browser. Yes, I know, there are game portals out there which have it, and it has been done before. I just want to do it for myself, seems like a massive, but fun, project.

I found a great post on making a map, and it is definitely something I'll check out for the design phase of the project. I've started designing the JavaScript part of the application though, and along the way I thought I'd post about object oriented JavaScript and inheritance.

On his site, Crockford describes JavaScript as "... a class-free, object-oriented language, and as such, it uses prototypal inheritance instead of classical inheritance" - no better way to say it. So what does this mean for me when I'm designing a Risk game?

One example is in the design for the military. In classical Risk, we have 3 types of military units: infantry, cavalry, and artillery. In my initial design, I had them as a JSON object:

'armyUnits': {
  	'Infantry': 1,
  	'Cavalry': 5,
  	'Artillery': 10,
  },

My thought was to iterate over them, using the key/value pairs to create the pieces on the board, adding methods to the instances created. This will turn into soup. This structure actually lends itself to an inheritance-based model. So let's take a look at how that could be built.

First, we need a parent 'class'. Again, no real classes in JavaScript, just functions, so we can start with something basic like this:

var military = function() {
}

var unit = new military();

console.log(unit, typeof unit)
•> military {} "object"

Great, so now we have the parent class, and you can see we have instantiated it with the keyword 'new', and created an object from it. This is cool and all, but not very useful yet. Now we need to think about what attributes all of these units will share and can be inherited, and which attributes will be unit-specific, and providing private methods to get and set those.

So let's take movement, strength, color, and unitName for our attributes. Of these, probably only movement can be inherited, while the others are unit-specific. Additionally, we probably want one of the three to be the default, if no options are passed to the class at instantiation. Knowing all this, if we take another pass at our parent class, it might look something like this:

var military = function(uName) {
  //unit name - artillery, cavalry or infantry
  this.unitName = uName == undefined ? 'Infantry' : uName.toString(),
  this.movement = 1,
  this.color = 'gray',
  this.strength = 1;

}

Great, so now we can define a new unit and it's slightly more meaningful:

var a = new military();

console.log(a, a.strength);
military {unitName: "Infantry", movement: 1, color: "gray", strength: 1} 1
var b = new military('cavalry');
console.log(b, b.strength);
military {unitName: "cavalry", movement: 1, color: "gray", strength: 1} 1

So you can see we already have a problem. All of our units are inheriting, but they're inheriting everything the same, which is not what we want. A cavalry unit has a strength of 5, so let's fix that.

var military = function(uName) {

this.unitName = uName == undefined ? 'Infantry' : uName.toString(),
this.movement = 1,
this.color = 'gray',
this.strength = 1,
this.getStrength = function() {
  return this.strength;
  },
this.setStrength = function() {
  if(this.unitName == 'Cavalry'){
    return this.strength = 5;
  }
  else if(this.unitName == 'Artillery'){
    return this.strength = 10;
  }
  else {
    return this.strength;
  }
};


}

Right, now we have getter/setters for the strength attributes, and it's conditional based on the name of the unit we pass into the class. This all looks to be good, so now let's take a look at the inheritance piece.

We'll create unit-specific classes - like 'cavalry', and then there are a couple of ways to enable the inheritance. We can use apply(), which takes an array of arguments

var cavalry = function() {
  military.apply(this, ['Cavalry']);
}
var c = new cavalry();
console.log(c, typeof c);
•> cavalry {unitName: "Cavalry", movement: 1, color: "gray", strength: 5} "object"

Awesome! We have created a new military unit, of the type 'cavalry'. Notice our strength is 5, which is what we wanted for that type. Another way we can inherit from military is to use call(), probably better in our case, as it accepts a comma-separated list of strings for the arguments. That looks something like this:

var cavalry = function() {
  military.call(this, 'Cavalry'); //we can add additional args here, ', 'Bob', 'bail of hay', etc..' and it won't affect our class at present. they will just be lost
  this.strength = this.setStrength();
}
var c = new cavalry();
console.log(c, typeof c);
•> cavalry {unitName: "Cavalry", movement: 1, color: "gray", strength: 5}color: "gray"getStrength: ()movement: 1setStrength: ()strength: 5unitName: "Cavalry"__proto__: cavalry "object"

So that's fine too. We have a cavalry unit with a strength of 5. So probably the last thing we will want to do is create some instances of these classes and then have the ability to extend them. If I have the need to add a 'hungry' variable into the cavalry class for example, I want to be able to do so once and have it apply to all instances I've created.

So let's take a look at that.

var horse = new cavalry();
var superHorse = new cavalry();
// now to extend
superHorse.isHungry = function() { console.log('wheres my food?'); }
// call it
superHorse.isHungry();
•> wheres my food?

//call it
horse.isHungry();
Uncaught TypeError: horse.isHungry is not a function(…)

So that did and didn't work. It worked for the one object - superHorse, because isHungry() is only acting as a property added to a specific object (kind of a static method), rather than it being an extension of the cavalry constructor, so it could apply to all instances. So let's make that happen, with a simple change.

cavalry.prototype.isHungry = function() { console.log('I am HUUUUNGRY!'); }

And voila! By applying it to the prototype, it's now accessible to all instances, so let's see what happened to horse and superHorse:

horse.isHungry()
•> I am HUUUUNGRY!

superHorse.isHungry()
•> wheres my food?

Oooh, interesting, so horse object correctly received the method, but superHorse still shows the previous message. That's because it was previously applied as a method to superHorse, so the 'child' method is overriding the 'parent'. But if we look at the __proto__ property for it, we can see the constructor method in action:

superHorse.__proto__.isHungry()
•> I am HUUUUNGRY!

Speaking of hunger, I'm off to lunch, and then on to more of Risk.