Inventory Items and prototypal inheritance in Javascript

by martin 18 January, 2015

Having a bunch of items in Polo’s House game inventory, requires a lot of typing and the majority of the items are quite similar. For example an apple, a pear, other fruits, share most of the same features.

For instance, we can define an apple item in JSON (which can be converted to a JS object with ease using JSON.parse()) like this:

 {
      "type"       : "food",
      "name"       : "apple",
      "display"    : "apple",
      "description": "shiny red apple",
      "image"      : "apple.png",

      "..."        : ".. other stuff excluded for simplicity ...",

      "activity"   : "Eat",
      "animation"  : {
        "names": "..."
       }
  }

Wouldn’t be great if we can reuse the definition of the apple in other fruit? Well, this is Javascript, we can take advantage of the prototypal inheritance.

Javascript at it’s core uses prototypal inheritance, which comes very handy in this situation. Most of us overlook it and use classical inheritance like in C++, Java, etc. If you want to read more about this please refer to the wonderful book by Eric Elliot Programming Javascript Applications

Using items as prototype or how to define a jalapeño

To define a new vegetable item, we would like to specify just its differences with a prototype object. Let’s do that for spicy Jalapeño pepper, using apple as the base:

{
      "delegate"   : "apple",
      "name"       : "jalapeno",
      "display"    : "Jalapeno",
      "description": "Red Hot Spicy Jalapeño",
      "image"      : "jalapeno.png"
 }

Now we have a new Jalapeño item, based on apple item. How does the engine knows which one is the prototype? Reading the delegate property.

Loading the JSON definitions

Everything seems nice, but we need a way to convert all this JSON definitions into real JS objects. If we just do a JSON.parse() that will not work, because `JSON.parse() will instance it and we get an object without the correct prototype and with partial properties.

We need to convert it after the JSON parse(). There are many ways to do this. I found it straightforward to use Lodash (there are alternatives to this lib: underscore, lazy.js, etc).

I assume you read about prototypal inheritance. One way to do this, will be cloning the prototype object, and adding the jalapeño definitions from the JSON:

var apple = JSON.parse(appleFromJSON);
var jalapeño = _.extend({}, apple, jalapenoFromJSON);

This should work, but if we have many of these objects, they will have a lot of information repeated wich is not very DRYish.

Flyweight Pattern for nothing, chicks for free

The Flyweight pattern comes to the rescue. The main idea, is to have all the prototype information in one place (in the delegate object or parent). We don’t want to clone the base object, we will just reference it from the jalapeño object. This will save some memory and will have the shared properties in one place (the prototype object).

We can use a shortcut to do it, using Object.create()

var jalapenoPrototype = Object.create(apple);
var jalapeno = _.extend(jalapenoPrototype, jalapenoFromJSON);

and That’s it! now the object is instanced with some properties, the prototype is also available:

jalapeno.name                 // shows 'jalapeno'
jalapeno.activity             // shows ''Eat'

jalapeno.type = "poison"     // Will create a property in the scope and will not modify the prototype
apple.type                     // still showing 'food'

jalapeno.__proto__.type = "poison";
apple.type                     // Now it will show 'poison'

Conclusion and limitations

Is not possible to instance properties in the delegate prototype, without affecting the “children” objects. One possibility would be treat the prototype as a base object and clone it, so the inventory item will behave as a child and share this unique prototype. This works fine for an inventory implementation, where the objects don’t change much.

var applePrototype = Object.create(appleFromJSON);
var apple = _.extend(applePrototype,{});

Well that’s it, questions, suggestions, leave a comment or contact me. For further reading about propotypal inherintace.

Thanks to Martin Muda, for editing this post.

comments powered by Disqus