Does JavaScript need method overloading?
John Resig of Mozilla and jQuery fame has a very interesting post about method overloading in JavsScript. In a nutshell, he proposes a utility function that gives a relatively simple way of overloading a method. The different versions are distinguished by the number of arguments (not their types). Another way of seeing it is that he factors out into a single place code that would usually be at the start of the method. Go check it out, it's a really interesting use of closures. I'll wait until you're done.
http://ejohn.org/blog/javascript-method-overloading/
OK, so that's pretty clever. But there are a few things that are wrong with that in my (probably not so) humble opinion.
- There is one additional level of indirection for each new overload. If the overload you're calling was the last added, you only have one additional call, but if you have, say, five overloads, the first one will have a stack trace that's five levels deeper than necessary. This makes it more painful to debug (of course all those additional functions will appear anonymous in the stack) and hurts performance. One of the commenters on John's post has a workaround but it requires that all overloads for a given method be defined in one single place (which may be all right). That one's probably not that bad.
- Any use of the arguments pseudo-array severely hurts performance.
- It's a lot more difficult to tool and to reflect on. IDEs, even if they have JavaScript IntelliSense, will have no clue what the signature(s) of that method is without additional metadata. If you get a reference to one of those methods, you can't determine its signature. Its length will always be zero. You can't toString it either as this will get you the addMethod code instead of the code of the method.
This last point is by far the most important. IntelliSense is really starting to take off in JavaScript as IDEs like Visual Studio 2008 or Aptana have pretty good support for it. The IntelliSense support could be restored if the developer provides additional metadata but that would need time and standardization of some form (OpenAjax could help here) and we have many other tools that rely on reflection that would be broken by this anyway (class browsers, rule-based code verification tools, etc.).
The real question I'd like to ask is the following. Is it worth the trouble?
As John mentions, EcmaScript 4 has built-in method overloading and to be perfectly clear I don't have a problem with overloading in general and if ES4 succeeds I'd have no problem using that feature. I just think this is a case where trying to mimic other languages in ES3 for the sake of it is just not worth the trouble as there are plenty of good or not so good alternatives:
- Named parameters: Typically, your method takes a single Object parameter, and you use something like myObject.myMethod({ foo: "bar", baz: 42}) to call it.
Pros: Completely free-form parameter list, both in number and type. Expressiveness and readability of calling code.
Cons: More difficult to tool without additional metadata (how do you know what named parameters are allowed, what their types are, what validation constraints they might have?). - Optional parameters: Some parameters can be omitted when calling the method.
Pros: Extremely simple to set up. Just test each parameter before you use it like you should anyway. Toolable.
Cons: Does not allow arbitrary combinations of parameters. Less expressive calling code than 1. - Naming overloads differently: have "findByName", "findByCity", etc. instead of just "find".
Pros: Expressive, simple. Allows for all forms of overloading.
Cons: Object model is larger, which means your member list may be larger than you'd like in IntelliSense. - Inspecting parameters from the method code: Basically, you just look at the arguments pseudo-array and do different things depending on the number and types of the parameters. There is only one method in this case.
Pros: This is the closest thing to real overloading. Allows for both number and types of parameters to be overloaded.
Cons: Poor performance. Tooling requires additional metadata. Reflection doesn't work. Pretty much the same cons as John's hack, only clumsier to write.
In Microsoft Ajax, we use a combination of options 2 and 3 and it's worked great so far. I've always liked the expressiveness of option 1 (code like foo("bar", true, false, true) is always hard to read as you have no idea just from that code what each boolean is for whereas foo({what:"bar", validateBaz: true, notifyListeners: false, doAdditionalStuff: true}) is a lot easier to understand (or would be if my example made any sense)) but we've stayed clear of it because of the tooling issue.
What do you think? Is overloading such a useful feature that it warrants losing some tooling?