Before I start, I want to go on the record to say that I do not agree with using function overloads in javascript. Javascript doesn’t really support function overloading, and overloaded functions (at least in my experience)┬áseem to lead to unclear code; the better solution is to pass in an object and determine behaviour from that.

That said, the other day I did have to implement function overloading, for a new project I started which aims to simplify basic animations when using paper.js. I wanted my solution to integrate nicely with existing code written for paper, so I decided to use a proxy object which intercepts the tranform function calls on an item and animates it, and for this reason my proxy object needed to implement matching method signatures.

Typically when people use function overloading in javascript, they are dealing with different numbers of arguments, but paper.js complicates things slightly as there can be ambiguities in function calls which can be resolved not by counting the number of arguments but by looking at the “types” of the arguments.

  • scale(Number:scale [, Point:center])
  • scale(Number:hor, Number:ver[, Point:center])

When calling something.scale(a,b), we cannot know which function to use without further inspecting b. I briefly googled function overloading in javascript and came up with a few solutions that dealt with overloads which had different numbers of overloads, but nothing that dealt with ambiguities such as the one above, and so I came up with this method:

function selectOverload(that, args, overloads) {
	var types = {
		Number: function(val) { return !isNaN(parseFloat(val)) && isFinite(val); },
		Point: function(val) { return val.x !== undefined && val.y !== undefined; }
	};
	for (var o = 0; o < overloads.length; o++) {
		var overload = overloads[o],
			matches = true;
		if (args.length > overloads[o].params.length) continue;
		for (var a = 0; a < args.length; a++) {
			if (!types[overload.params[a]](args[a])) {
				matches = false;
				break;
			};
		}
		if (matches) { return overload.fn.apply(that, args); }
	}
}

And it gets used like this:

AnimationProxy.prototype.scale = function() {
	selectOverload(
		this, arguments,
		[{
			params: ["Number","Point"],
			fn: function(scale, center) {
				// code for this overload here
			}
		},
		{
			params: ["Number","Number","Point"],
			fn: function(hor, ver, center) {
				// code for this overload here
			}
		}]
	);
	return this;
}

Although not perfect, I’m pretty pleased with this code. I think the intention is pretty clear, and like that the arguments are correctly named. It doesn’t actually check the type of the argument; the “type definitions” are actually just boolean functions which return true or false if an argument meets their “type criteria”, and so this can easily be used on a whole variety of scenarios.

Also, I made the decision to pass a string value in for the type and have the type defs in the selectOverload function, but that’s just because it suited the situation I was working with, but there’s no reason you can’t adapt the code to use the criteria function itself. In my situation I didn’t feel it would be as readable, but if you have a lot of different types then it may work out more efficient.