Thursday 11 April 2013

Decorate With Defaults Checking/Setting

The other day I ended up with a design where I would have a bunch of JavaScript functions where all of them would have a number of optional parameters that if not provided should be set to some configuration defined defaults. So I would have to repeat the two first lines in the function below in all these related functions


function getValue(x, y, server, credentials){
   server = server || Config.server;
   credentials = credentials || Config.credentials
   ...
}

Repeating this inside every function adds quite unnecessary noise, so thinking of a way to improve it I ended up writing a generic decorateWithDefaultChecking function. Nothing too interesting, we have a factory function that returns a new function that takes care of the defaults checking and then invokes the original function, but anyway I thought I would share it here:

//decorates a given function with checking/assignment of default values to undefined parameters
function decorateWithDefaultsChecking(targetFunc /* ,default1, default2... */){
  var defaults = [].slice.call(arguments, 1)
 if (!defaults)
  return targetFunc;
 var opStart = targetFunc.length - defaults.length;
 return function(){
  for(i=0; i<defaults.length; i++){
   if (arguments[opStart + i] === undefined){
    arguments[opStart + i] = defaults[i];
   }
  }
  //the next line below here is essential, remember that arguments is not a real array, but an "array like" object, 
  //so in order for this to work we need to set the length property on our own, otherwise, the new assigned values are ignored when invoking apply
  arguments.length = arguments.length + defaults.length;
  return targetFunc.apply(this, arguments);
 };
}

The most interesting point is one more JavaScript quirk that I stumbled upon. I've already posted about the beautiful arguments variable available inside each function, and already mentioned before that it's not a real Array, but an Array-like object. Well, in this case, its Array-like nature means we need to set its length property accordingly. Otherwise, though the addition of new elements to the pseudo-array takes place, they won't be passed to the function invoked via Function.prototype.apply

.

No comments:

Post a Comment