JavaScript for C# developers: the Module Pattern (part 2)

Last time I talked about the simple module pattern. This is where you create a function that returns an object with behavior and state and that behavior and state is implemented (and made private) by using a closure. We showed this by using the module pattern to create a stopwatch object.

StopwatchLet’s now see how we can extend this stopwatch object by adding the facility to have lap times. This gives us the ability to use the same stopwatch to time a sequence of time-consuming actions, rather than creating a new stopwatch every time. The only caveat is that we can’t modify any of the code we’ve already written.

We’ll assume then that we have a new stopwatch object created from the previous code. We need to add a lap() method to record the time since the start or since the last lap. We also need a reportLaps() method that will return an array of lap times. One way to do this would be to create a brand new wrapper object (call it, say, a lapwatch) that uses the already created stopwatch internally as a delegate. In other words, the new lapwatch object would delegate all timings to the internal stopwatch but keep its own lap times. Perfectly doable but it’s hardly extending the original stopwatch object.

What we’ll do is to apply the module pattern again, but this time not to create a new object. Instead we shall augment the existing object. So the first change is to pass in the existing object to our anonymous function.

(function (sw) {

  // some code that operates on sw

}(stopwatch));

We have another auto-executing anonymous function, but this time it takes a single parameter: the stopwatch object. Inside the function, the parameter is known as sw for convenience’ sake. We assume that the as-yet-unwritten code will be modifying sw (and hence the external stopwatch). You could also return sw and re-assign it to stopwatch if you wanted, much like we did previously.

Now we can write the code that provides and reports the lap times.

(function (sw) {
  var laptimes = [];

  sw.lap = function () {
    laptimes.push(sw.stop());
    sw.start();
  };

  sw.reportLaps = function () {
    var laps = laptimes;
    laptimes = [];
    return laps;
  };
}(stopwatch));

As you can see, we have a local array to store the lap times and we add the two new methods to the existing object. The function forms another closure and provides a new private variable. The lap() method is a bit of a hack: since I stipulated that we couldn’t change the original object and since we have no access to the startTime variable, I had to stop the stopwatch to get the elapsed time and then immediately start it again. A better solution perhaps would have been to add a Peek() method to the original code, just so we can see the current elapsed time without stopping the stopwatch.

And here’s some dummy code that exercises this augmented stopwatch:

var i;
stopwatch.start();
for (i = 0; i < 100000; i++);
stopwatch.lap();
for (i = 0; i < 200000; i++);
stopwatch.lap();
for (i = 0; i < 300000; i++);
stopwatch.lap();
console.log(stopwatch.reportLaps());

This pattern is typically known as the Tightly Augmented Module Pattern. We pass in the current object and the function forms another closure over it to modify it. For this code to work we *must* declare the original code first and then this code. If they are in different source code files (the usual case), we must declare the stopwatch code file first, and then this augmented stopwatch code second. That way, the JavaScript interpreter will execute the code in the proper order; the augmentation code won’t get run on an undefined variable.

A slight modification that we can’t really show with our stopwatch example is the Loosely Augmented Module Pattern. With this pattern we’re usually building some kind of utility object or a namespace that contains a whole bunch of other objects that do some work. These other objects don’t require or interact with each other. For example:

var jmbNamepace = (function ($j) {
  $j.date = { ... };
}(jmbNamespace || {}));

--- 

var jmbNamepace = (function ($j) {
  $j.regex = { ... };
}(jmbNamespace || {}));
 
---

var jmbNamepace = (function ($j) {
  $j.url = { ... };
}(jmbNamespace || {}));

Each of these three code segments could be run before any of the others, and each could be omitted, if needed. If they were all in different source files, those source files could be loaded in any order, and only those needed could be loaded. The magic is in the expression jmbNamespace || {}. What this says is “evaluate the expression to be equal to jmbNamespace if it is defined, otherwise evaluate to an empty object”.

Going back to our stopwatch example, notice that there’s something buggy about it. If I’d called stop() on the augmented stopwatch at the end of the last “lap”, the time for it won’t be recorded in the array of lap times. We should allow for this possibility. The way to do this is through an override: we have to override the behavior of stop() if we are using the stopwatch to time laps. Here’s how to do that (and note I’ve changed the definition of the anonymous function to return the augmented stopwatch object to show that this is an equally valid application of the Tightly Augmented Module Pattern):

var stopwatch = (function (sw) {
  var laptimes = [];
  var oldStop = sw.stop;

  sw.stop = function () {
    laptimes.push(oldStop());
  };

  sw.lap = function () {
    sw.stop();
    sw.start();
  };

  sw.reportLaps = function () {
    var laps = laptimes;
    laptimes = [];
    return laps;
  };

  return sw;
}(stopwatch));

The first new thing that happens is that we copy the function object referenced by the stopwatch’s stop() method and save it in the local variable oldStop. (The joys of first-class functions or “functions are objects”!) We then replace the stop() method with a new one that pushes the final lap time onto the array and we call the old stop function to get that final lap time. The lap() method also has to change: we can now just call the stopwatch object’s stop() method to do our work since it’s now rerouted through our override. All these shenanigans are of course hidden from view inside the closure.

Next time we’ll close off this mini-series on the module pattern with some final features.

(Part 1, Part 3 of this series.)

Album cover for The Best of the Art of NoiseNow playing:
Art of Noise - Peter Gunn [The Twang Mix]
(from The Best of the Art of Noise)


Loading similar posts...   Loading links to posts on similar topics...

No Responses

Feel free to add a comment...

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response