JavaScript Method Overloading


In a side project that I’ve been working on I built a quick-and-dirty function for doing simple method overloading. For those of you who aren’t familiar with, it’s just a way of mapping a single function call to multiple functions based upon the arguments they accept.

Here’s the function in question:

  1. // addMethod - By John Resig (MIT Licensed)
  2. function addMethod(object, name, fn){
  3.     var old = object[ name ];
  4.     object[ name ] = function(){
  5.         if ( fn.length == arguments.length )
  6.             return fn.apply( this, arguments );
  7.         else if ( typeof old == 'function' )
  8.             return old.apply( this, arguments );
  9.     };
  10. }

and here is how you might use it:

  1. function Users(){
  2.   addMethod(this, "find", function(){
  3.     // Find all users...
  4.   });
  5.   addMethod(this, "find", function(name){
  6.     // Find a user by name
  7.   });
  8.   addMethod(this, "find", function(first, last){
  9.     // Find a user by first and last name
  10.   });
  11. }

Or, if you wanted to use it with an object prototype:

  1. function Users(){}
  2. addMethod(Users.prototype, "find", function(){
  3.   // Find all users...
  4. });
  5. addMethod(Users.prototype, "find", function(name){
  6.   // Find a user by name
  7. });
  8. addMethod(Users.prototype, "find", function(first, last){
  9.   // Find a user by first and last name
  10. });

And here’s what the end result would look like to the user:

  1. var users = new Users();
  2. users.find(); // Finds all
  3. users.find("John"); // Finds users by name
  4. users.find("John", "Resig"); // Finds users by first and last name
  5. users.find("John", "E", "Resig"); // Does nothing

Obviously, there’s some pretty big caveats when using this:

  • The overloading only works for different numbers of arguments – it doesn’t differentiate based on type, argument names, or anything else. (ECMAScript 4/JavaScript 2, however, will have this ability – called Multimethods – I’m quite excited.)
  • All methods will some function call overhead. Thus, you’ll want to take that into consideration in high performance situations.

Now, the secret sauce is all going back to the fn.length expression. This isn’t very well known, but all functions have a length property on them. This property equates to the number of arguments that the function is expecting. Thus, if you define a function that accepts a single argument, it’ll have a length of 1, like so:

(function(foo){}).length == 1

I did some basic testing and this technique seems to work in all modern browsers – please let me know if I’m wrong.

If you’re concerned about adding a function call overhead when binding only a single function, then you can give this version of addMethod a try:

  1. // addMethod - By John Resig (MIT Licensed)
  2. function addMethod(object, name, fn){
  3.     var old = object[ name ];
  4.     if ( old )
  5.         object[ name ] = function(){
  6.             if ( fn.length == arguments.length )
  7.                 return fn.apply( this, arguments );
  8.             else if ( typeof old == 'function' )
  9.                 return old.apply( this, arguments );
  10.         };
  11.     else
  12.         object[ name ] = fn;
  13. }

That one will attach the first bound function with no additional checks – keeping it nice and speedy. Once extra functions are bound, things will slow ever so slightly.

This, also, has the added benefit of being able to bind default “catch all” functions which will handle all the calls that pass through. The result would look something like this:

  1. var users = new Users();
  2. users.find(); // Finds all
  3. users.find("John"); // Finds users by name
  4. users.find("John", "Resig"); // Finds users by first and last name
  5. users.find("John", "E", "Resig"); // Finds all

This function won’t change the world, but it’s short, concise, and uses an obscure JavaScript feature – so it wins in my book.

Posted: November 13th, 2007


If you particularly enjoy my work, I appreciate donations given with Gittip.

34 Comments (Show Comments)



Comments are closed.
Comments are automatically turned off two weeks after the original post. If you have a question concerning the content of this post, please feel free to contact me.


Secrets of the JavaScript Ninja

Secrets of the JS Ninja

Secret techniques of top JavaScript programmers. Published by Manning.

Ukiyo-e Database and Search

Ukiyo-e.org

Japanese woodblock print database and search engine.


John Resig Twitter Updates

@jeresig

Infrequent, short, updates and links.


via Ad Packs