Wrapping Functions in JavaScript

I find myself wrapping JavaScript functions more these days to add aspect-oriented behavior. One JavaScript file can define a function and another file can add more behavior.

Problem

Suppose we have a relatively standard library function for attaching event handlers to elements that works with both the DOM2 and Internet Explorer event models.

var LIB = {};

LIB.on = function(element, type, callback) {
  // ...
};

A call to this may be something like

LIB.on(document.getElementById('myElement'),
       'click',
       function(e) {alert('I was clicked');});

Now suppose we want to add aspect-oriented logging to our library to help with development debugging.

We could add development code above and then have some program to strip out that development code before production. These sorts of feature requests are exactly how small libraries become bloated and too big for anyone's use.

Another way is to leave the code above alone and wrap the function referenced by LIB.on above with a new function. This new function is assigned to LIB.on. Two ways of doing this wrapping are shown below.

Solution 1

LIB.on = (function() {
  var original = LIB.on;
  return function(element, type, callback) {
    console.log('adding ' + type + ' listener');
    return original(element, type, callback);
  };
})();

This method doesn't add any new symbols to the global or LIB spaces. After the wrapping occurs there is no way to access the original function. It is only caught in the closure of the new wrapper function.

Solution 2

LIB.onWithoutLogging = LIB.on;

LIB.on = function(element, type, callback) {
  console.log('adding ' + type + ' listener');
  return LIB.onWithoutLogging(element, type, callback);
};

This method adds a new symbol to the LIB space. The original LIB.on function is still available by calling LIB.onWithoutLogging. Also this pattern allows for the redefinition of LIB.onWithoutLogging, even after the wrapping has occured. This is thanks to the late binding to the original function in the wrapped function. I've seen this pattern quite frequently in Ruby code where Ruby's alias_method is used.

Conclusion

I've found these patterns quite useful for building up layers of functionality and avoiding the bloat of a library that tries to do everything for everyone. With JavaScript's dynamic nature demonstrated above, it is possible to build modular library's were the developer can include only those necessary for his needs.

Comments

Have something to write? Comment on this article.

kangax April 25, 2008

We use this technique (via Function.prototype.wrap) in prototype.js quite often. It is indeed a great way to prevent bloat. I even wrote an article on this subject http://thinkweb2.com/projects/prototype/wrap-it-up/

As a side note, I find passing original function into a "wrapping" one a little cleaner (i.e. the closure is created implicitly):

...
Lib.on = (function(proceed){
  return function(){
    console.log('before');
    return proceed.apply(proceed, arguments)
  }
})(Lib.on);

Best,
kangax

Have something to write? Comment on this article.