blog

Image of stamps by Snapshots of The Past from Flickr

Stampit: Prototypal JavaScript Made Easy

by | Aug 15, 2013 | Developer Blog | 3 comments

I’ve been writing heavy-duty JavaScript for about six years. I’ve used lots of libraries that provide “classes” of various types—ExtJS, Dojo, JavaScriptMVC, and more. Yet none of them felt right. In retrospect, I feel like the standard class-based inheritance is not good fit for the language. Perhaps that’s because classical inheritance can be shoe-horned into JavaScript, butprototypal inheritance is how the language is built.

The other day, I ran across someone else with the same attitude. In fact, while I was thinking about it, he was talking and writing code to make it real. He is Eric Elliot and he wrote Stampit.

Stampit

Stampit is an interesting and small library designed to make it easy to use prototypal inheritance. In a game that I ported from Python to JavaScript, I used prototypal inheritance in the traditional way:

// Not exactly the code you’ll find in the game, but close enough for
// illustration.
//
FunnyBoat.Boat = function(application, game) {
    // ...
}
FunnyBoat.Boat.prototype = {
   ahead: function(x) { /* ... */ },
   // ...
}

I had several diffent “actor” classes (think actors on a stage, not the actor pattern) that had different other roles. One of the player’s character—a boat. Another was a pirate ship. Still another was a jumping shark. These have some things in common: floating, sinking, and dealing and taking damage. They have some differences: jumping, shooting, and controling inputs.

It became clear that it would be useful to build the actors by composition. I eventually hacked together some solutions that worked, but were not elegant. When I saw Stampit, I knew something better had come.

Stampit has a handy compose method that applies the other Stampit methods to work composition magic. First, I’ll start with a naive state machine for jumping actors:

/*jshint asi:true*/
var stampit = require('stampit')
var jumpingActor = stampit().enclose(function() {
    var kJumpDuration = 2 * 1000 // ms
    var kStateDown = 'down'
    var kStateUp = 'up'
    var jumpTimer = null
    var state = kStateDown
    function OnFinishJump() {
        // assert kStateUp == state
        jumpTimer = null
        state = kStateDown
    }
    this.Jump = function() {
        if (kStateDown == state) {
            jumpTimer = setTimeout(OnFinishJump, kJumpDuration)
            state = kStateUp
        }
    }
    this.GetJumpingState = function() {
        return state
    }
})

There is no rocket science in this code. There is a shared private state variable, two public methods, and a private method. Next, I’ll add a similarly naive state machine for shooting:

    var shootingActor = stampit().enclose(function() {
    var kReloadDuration = 3 * 1000 // ms
    var kStateReadyToShoot = 'ready'
    var kStateReloading = 'reloading'
    var reloadingTimer = null
    var state = kStateReadyToShoot
    function OnFinishReload() {
        // assert kStateReloading == state
        reloadTimer = null
        state = kStateReadyToShoot
    }
    this.Shoot = function() {
        if (kStateReadyToShoot == state) {
            reloadTimer = setTimeout(OnFinishReload, kReloadDuration)
            state = kStateReloading
        }
    }
    this.GetShootingState = function() {
        return state
    }
})

Note that I’m using the state variable name again. If you were in a traditional class, you would have to use separate state names (or better yet, separate state machine objects.)

Here’s a simple function for logging the states:

var stateReporting = stampit.convertConstructor(function() {
    return stampit.extend(this, {
        ReportStates: function() {
            console.log(this.GetJumpingState(), this.GetShootingState())
        }
    })
})

Using the Stampit compose method, we create a factory that puts all these together in a single object, and test the code:

var composedActorFactory = stampit.compose(
    jumpingActor,
    shootingActor,
    stateReporting)
global.actor = composedActorFactory()
actor.ReportStates()
actor.Shoot()
actor.ReportStates()
actor.Jump()
actor.ReportStates()
setTimeout(function() {actor.ReportStates()}, 2500)
setTimeout(function() {actor.ReportStates()}, 3500)

When I run it, I see this:

$ node demo.js
down ready
down reloading
up reloading
down reloading
down ready

Yay for encapsulation and composition in a pretty format! For a slightly deeper walk-through and more information on why should consider prototypal inheritance (with or without Stampit), see Eric Elliot’s page on Three Kinds of Prototypal OO.

+ more

Accurate Timing

Accurate Timing

In many tasks we need to do something at given intervals of time. The most obvious ways may not give you the best results. Time? Meh. The most basic tasks that don't have what you might call CPU-scale time requirements can be handled with the usual language and...

read more