Method overloading is one of the nice things that exists in real programming languages such as Java. Even though JavaScript starts with “Java”, there are very few similarities between the two languages. Still, due to the power of the scripting language, there is a way to overload functions. The most commonly used method is to simply write some if statements to examine the arguments. Still, I thought it would be nice to be able to overload functions in a more natural way. Therefore, I wrote the following prototype function for functions which will be included in the next version of jPaq:

// Function.prototype.overload - By Chris West - MIT Licensed
Function.prototype.overload = function(fnToAdd, objFilters) {
  var fnOriginal = this;
  return function() {
    var fnToUse = fnToAdd, args = arguments, len = args.length;
    if(objFilters) {
      for(var key in objFilters) {
        if((key == "all" && !objFilters[key].call(this, args))
           || (key == "length" && len != objFilters[key])
           || (/^\d+$/.test(key) && !objFilters[key](args[key]))) {
          fnToUse = fnOriginal;
          break;
        }
      }
    }
    else if(fnToAdd.length !== len) {
      fnToUse = fnOriginal;
    }
    return fnToUse.apply(this, args);
  };
};

First Parameter (fnToAdd)

The first parameter that you pass in must be the function that you want to overload the current function.

Second Parameter (objFilters)

The second parameter is optional. If it is not specified, the only way that fnToAdd will be used is if the amount of arguments specified in the header of that function matches the number of arguments passed in. If the second parameter is specified, it must be an object literal which will be used to determine whether or not to use this function instead of the original. To filter based on a specific parameter, you should make the key be the index of the parameter and the value should be the function that will check that parameter. This filter function will only receive the specified parameter as its only argument. If you want to filter by the amount of parameters passed, you can define the length of the object literal.  If you want use an all-purpose filter function, you can define the all property of the object literal as a function which will receive all of the parameters just as the function would.  In addition, this all-purpose filter function will also receive the this object of the function.  Just as in the case of the other filter functions, if the return value evaluates to false, the original function will be used, otherwise the new function will be used.

Test Code

Now, instead of trying to clarify the above description with more words, let’s let the code do the talking. Assuming that the definition for Function.prototype.overload() appears before this code, the following will test and prove that function overloading can be quite easy:

// Returns a boolean indicating whether or not the argument is an array.
function isArray(arg) {
  return Object.prototype.toString.call(arg) == "[object Array]";
}

// Returns a boolean indicating whether or not the argument is a number.
function isNumber(arg) {
  return Object.prototype.toString.call(arg) == "[object Number]";
}

// The default callAny() function.
function callAny() {
  alert("Default callAny() function was called.");
};

// The callAny() function that will be used if the first parameter is an array.
function callArray(arr) {
  alert("callArray() called with [" + arr.join(", ") + "]");
}

// The callAny() function that will be used if two numbers are passed.
function callNum(num1, num2) {
  alert("callNum() called with " + num1 + " and " + num2);
}

// The callAny function that will be used if no arguments are passed.
function callNone() {
  alert("callNone() called without arguments as expected.");
}

// Easily overload the callAny() function.
callAny = callAny
  .overload(callNone)
  .overload(callArray, {0 : isArray})
  .overload(callNum, {0 : isNumber, length : 2});

/********** Example calls the callAny() **********/
// Calls the default function.
callAny();

// Calls the callNum() function.
callAny(1, 2);

// Calls the default function.
callAny(3);

// Calls the callArray() function.
callAny(["Hello", "World!!!"]);

The first thing to note is that the isNumber() and isArray() functions are defined to filter the arguments. Both callNone(), callArray() and callNum() will overload callAny(). If callAny() is called without any parameters, it acts as if callNone() was invoked. If callAny() is called with the first parameter being an array, it acts as if callArray() was called. If callAny() is called with the first parameter being a number and is called with exactly two parameters, it acts as if callNum() was called. In all other cases the default version of callAny() is called.

Future

I personally think that this may become useful for other JavaScript programmers. Therefore, I plan on incorporating this function into jPaq. Unfortunately, I don’t have an exact date for the next release, therefore, I am fine with you using this code as long as you give me credit for my code. 8)


2 Comments

Barney Sidorowicz · September 21, 2011 at 8:19 PM

It’s a shame you don’t have a donate button! I’d most certainly donate to this fantastic blog! I guess for now i’ll settle for book-marking and adding your RSS feed to my Google account. I look forward to fresh updates and will share this site with my Facebook group. Chat soon!

Stamat · April 10, 2013 at 6:28 AM

I had a similar solution to the same problem. Maybe we could compare solutions and devise even better one. http://stamat.wordpress.com/2013/04/10/javascript-function-overloading/

Leave a Reply

Your email address will not be published. Required fields are marked *