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.

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:

  1. 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?).
  2. 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.
  3. 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.
  4. 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?


  • I agree with your take. Its something that'll be nice to have as a first class feature of the language. (this is actually one of the few ES4 things I'm looking forward to having) Its nowhere near a big enough deal for me to want a ES3 workaround, though.

  • I don't feel any need for hacked-in* overloading, but I do think its a good thing to be adding to the next version.

  • I am a big fan of IntelliSense but not at the expense of a programming language's expressiveness.

    I wonder if there would be a way for currying to support overloading methods by arity.

    Maybe use pattern matching like in F# to support overloading methods by arity and parameter datatype.

  • Here is a simple solution that supports multiple signatures differentiated by types, and it has a low overhead as well..

  • I posted an article about this, also discussing John Resig's version and offering an alternative. My proposed solution may fall prey to some of the same problems you mention but it offers the nifty ability to overload functions with type signatures, not just based on the number of parameters.

    I think some method overloading is nice, like how jQuery or MooTools will sometimes allow you to provide the String id of an HTML object or a reference to the object itself. But, it certainly isn't a necessary feature and adding additional overhead and boilerplate code for defining functions isn't ideal. So, perhaps the solution proposed in my article is best suited for theoretical discussion rather than production use.

Comments have been disabled for this content.