Guard Functions in JS Classes

July 24, 2015

Thought I’d write up a quick JavaScript refactor technique that’s come up in some code I’ve been working on lately.

For this example, assume we have a Client class. This Client has many methods, some of which are network-based, only intended to be used when the Client is connected. These methods also accept callbacks.

Our current implementation looks something like this:

var Client = function Client() {
  this.connected = false;
}

Client.prototype.connect = function() {
  this.connected = true;
}

Client.prototype.disconnect = function() {
  this.connected = false;
}

Client.prototype.fetch = function(callback) {
  if (!this.connected) {
    return callback("Not connected");
  }

  // ...
  callback(null, data);
}

Client.prototype.getDetails = function(callback) {
  if (!this.connected) {
    return callback("Not connected");
  }

  // ...
  callback(null, data);
}

Client.prototype.post = function(message, callback) {
  if (!this.connected) {
    return callback("Not connected");
  }

  // ...
  callback(null, data);
}

Now, this is at least a bit better than the slightly alternate approach these methods seem to take more often:

Client.prototype.post = function(message, callback) {
  if (this.connected) {
    // ...
    callback(null, data);
  } else {
    callback("Not connected");
  }
}

But either way we’re clogging up these methods with duplicated checking code that doesn’t really need to be there.

JS makes it trivial to construct functions that wrap other functions:

function wrapper(fn) {
  return function() {
    console.log("before");
    fn.apply(null, arguments);
    console.log("after");
  }
}

var wrapped = wrapper(function(arg) {
  console.log("during, with argument", arg);
});

wrapped("hello, world");
//=> before
//=> during, with argument hello, world
//=> after

It’s a fairly simple technique, but one that yields dividends, particularly when you start finding useful places to apply it.

Let’s do so now, and see if we can find a solution to this duplicated code:

function connected(fn) {
  return function() {
    // assume last argument is a callback
    var callback = arguments[arguments.length - 1];

    if (!this.connected) {
      return callback("Not connected");
    }

    return fn.apply(this, arguments);
  }
}

Still nothing too fancy going on here, but we can apply it to our class’ instance methods to make life a little easier:

Client.prototype.fetch = connected(function(callback) {
  // ...
  callback(null, data);
});

Client.prototype.getDetails = connected(function(callback) {
  // ...
  callback(null, data);
});

Client.prototype.post = connected(function(message, callback) {
  // ...
  callback(null, data);
});

Lo and behold, suddenly the bodies of these methods are focused on the actual work they need to perform!

This is still a pretty simple refactoring and technique, but little things like this make a difference. Applying similar techniques can help in many situations, so look out for them.

If you’re new(er) to JavaScript, or otherwise haven’t come across code like this before, I hope you found this helpful. I also cannot recommend Reginald Braithwaite’s book, JavaScript Allongé, highly enough.

If you write JS, and want to get better, you really do need to read this book. It’s even generously been made available free online, so no excuses.