There is no such thing as a JavaScript plugin

One thing that you often hear people say about jQuery is that is has a neat plugin mechanism. It’s true there are a great many useful plugins available for jQuery and it makes them super-easy to write, but I want to let you in on a little secret:

jQuery.fn = jQuery.prototype;

That is jQuery’s plugin mechanism in its entirity. Now my point here is not that jQuery somehow sucks (far from it) but that I suspect many folks could use JavaScript better if they understood how flexible it is. In jQuery’s case, the $() function returns an instance of the jQuery class, so if you add methods to jQuery.prototype then you can use those methods to change elements in new and exciting ways. Behold:

$.fn.makeThemRed = function() {
  this.css({color: 'red'});
  return this;
};

$('p').makeThemRed();

$.fn is just a shorter alias for jQuery.prototype. The really important thing here is that there is nothing special about the jQuery class: all objects (including prototypes) are extensible in JavaScript, and you can add and remove properties and methods from any object whenever you like. Of course, the effect of changes you make to a given object are entirely up to you, the point is that you are allowed to make such changes.

One consequence of this is that you can modify the behaviour of existing methods in any library you happen to be using. Take this snippet from Reiterate, which adds new behaviour to a stack of Enumerable methods in Prototype:

[Enumerable, Array.prototype, Hash.prototype,
ObjectRange.prototype, Ajax.Responders,
Element.ClassNames.prototype].each(function(object) {

  $w('each all any collect detect findAll max \
  min partition reject sortBy map find select \
  filter every some').each(function(method) {

    if (!object[method]) return;
    var wrapped = object[method];
    object[method] = function() {
      var args = $A(arguments);
      if (args[0]) args[0] = Function.from(args[0]);
      return wrapped.apply(this, args);
    };

  });
});

This loops over all objects that include Enumerable, and loops over the list of modified methods for each object. For example, lets’s expand it for the modification of Enumerable.collect to make it a little clearer:

    var wrapped = Enumerable.collect;
    Enumerable.collect = function() {
      var args = $A(arguments);
      if (args[0]) args[0] = Function.from(args[0]);
      return wrapped.apply(this, args);
    };

So we store a reference to the original implementation of Enumerable.collect, then redefine it. Our new function can call the original function after modifying the arguments a little – it effectively acts as an input filter. Some parts of this implementation (each(), $w(), $A()) are provided by Prototype but the core idea – the method interception – is pure JavaScript. Next time you’re using a fancy language feature provided by your favourite library, take a look at how it’s implemented; you’ll probably find the implementation is trivial and you might learn a little about JavaScript’s power.

If you’ve enjoyed this article, you might enjoy my recently published book JavaScript Testing Recipes. It’s full of simple techniques for writing modular, maintainable JavaScript apps in the browser and on the server.